feat(KB-060): add model selection to task creation

- Extend task creation types, store persistence, and API payloads to accept executor and validator model overrides
- Add inline model selection to the dashboard create card with default normalization, retryable model loading, and focus-safe interactions
- Wire the list-view create flow through InlineCreateCard and style the dropdown to stay usable within the viewport on mobile and desktop
- Add store, route, app, list-view, and inline-create tests covering override submission, partial selections, and inline creation behavior
- Document the new creation-time model selection flow and include a published package changeset
This commit is contained in:
gsxdsm
2026-03-30 05:41:30 -07:00
parent 5f10e3aac7
commit ea084c0ff2
15 changed files with 919 additions and 88 deletions

View File

@@ -654,6 +654,30 @@ describe("TaskStore", () => {
});
});
describe("createTask — model overrides", () => {
it("persists executor and validator model overrides on creation", async () => {
const created = await store.createTask({
title: "Task with model overrides",
description: "Use explicit executor and validator models",
modelProvider: "anthropic",
modelId: "claude-sonnet-4-5",
validatorModelProvider: "openai",
validatorModelId: "gpt-4o",
});
expect(created.modelProvider).toBe("anthropic");
expect(created.modelId).toBe("claude-sonnet-4-5");
expect(created.validatorModelProvider).toBe("openai");
expect(created.validatorModelId).toBe("gpt-4o");
const persisted = await store.getTask(created.id);
expect(persisted.modelProvider).toBe("anthropic");
expect(persisted.modelId).toBe("claude-sonnet-4-5");
expect(persisted.validatorModelProvider).toBe("openai");
expect(persisted.validatorModelId).toBe("gpt-4o");
});
});
describe("updateTask — model overrides", () => {
it("sets executor model provider and id via updateTask", async () => {
const task = await createTestTask();

View File

@@ -194,6 +194,10 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
column: input.column || "triage",
dependencies: input.dependencies || [],
breakIntoSubtasks: input.breakIntoSubtasks === true ? true : undefined,
modelProvider: input.modelProvider,
modelId: input.modelId,
validatorModelProvider: input.validatorModelProvider,
validatorModelId: input.validatorModelId,
steps: [],
currentStep: 0,
log: [{ timestamp: now, action: "Task created" }],

View File

@@ -173,6 +173,22 @@ export interface TaskCreateInput {
column?: Column;
dependencies?: string[];
breakIntoSubtasks?: boolean;
/** AI model provider override for the executor agent (e.g., "anthropic").
* Must be set together with `modelId`. When both model fields are undefined,
* the executor uses global settings defaults. */
modelProvider?: string;
/** AI model ID override for the executor agent (e.g., "claude-sonnet-4-5").
* Must be set together with `modelProvider`. When both model fields are undefined,
* the executor uses global settings defaults. */
modelId?: string;
/** AI model provider override for the validator/reviewer agent.
* Must be set together with `validatorModelId`. When both validator model fields
* are undefined, the reviewer uses global settings defaults. */
validatorModelProvider?: string;
/** AI model ID override for the validator/reviewer agent.
* Must be set together with `validatorModelProvider`. When both validator model
* fields are undefined, the reviewer uses global settings defaults. */
validatorModelId?: string;
}
export interface Settings {