Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
161 lines
4.8 KiB
TypeScript
161 lines
4.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
effectiveTaskAssignmentValue,
|
|
effectiveTaskFlag,
|
|
mergeTaskAssignmentDraft,
|
|
taskAssignmentInputsFromDrafts
|
|
} from "./tuttiModePlanTaskAssignments";
|
|
import type { TuttiModePlanPanelTaskViewModel } from "./tuttiModePlanPanelProjection";
|
|
|
|
function taskViewModel(
|
|
overrides: Partial<TuttiModePlanPanelTaskViewModel> & { id: string }
|
|
): TuttiModePlanPanelTaskViewModel {
|
|
return {
|
|
ordinal: 1,
|
|
title: "Task",
|
|
content: "",
|
|
priority: "medium",
|
|
agentTargetId: null,
|
|
modelPlanId: null,
|
|
model: null,
|
|
permissionModeId: null,
|
|
reasoningEffort: null,
|
|
executionDirectory: null,
|
|
dependsOn: [],
|
|
parallelizable: false,
|
|
autoAccept: false,
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
describe("mergeTaskAssignmentDraft", () => {
|
|
it("stores field edits per task", () => {
|
|
const drafts = mergeTaskAssignmentDraft({}, "task-1", {
|
|
reasoningEffort: "high"
|
|
});
|
|
expect(drafts["task-1"]).toEqual({ reasoningEffort: "high" });
|
|
});
|
|
|
|
it("resets dependent selections when the agent changes", () => {
|
|
let drafts = mergeTaskAssignmentDraft({}, "task-1", {
|
|
modelPlanId: "plan-1",
|
|
model: "model-1"
|
|
});
|
|
drafts = mergeTaskAssignmentDraft(drafts, "task-1", {
|
|
agentTargetId: "agent-2"
|
|
});
|
|
expect(drafts["task-1"]).toEqual({
|
|
agentTargetId: "agent-2",
|
|
modelPlanId: "",
|
|
model: "",
|
|
permissionModeId: "",
|
|
reasoningEffort: ""
|
|
});
|
|
});
|
|
|
|
it("keeps the agent-independent toggles across agent changes", () => {
|
|
let drafts = mergeTaskAssignmentDraft({}, "task-1", {
|
|
parallelizable: true
|
|
});
|
|
drafts = mergeTaskAssignmentDraft(drafts, "task-1", { autoAccept: true });
|
|
drafts = mergeTaskAssignmentDraft(drafts, "task-1", {
|
|
agentTargetId: "agent-2"
|
|
});
|
|
expect(drafts["task-1"]).toEqual({
|
|
agentTargetId: "agent-2",
|
|
modelPlanId: "",
|
|
model: "",
|
|
permissionModeId: "",
|
|
reasoningEffort: "",
|
|
parallelizable: true,
|
|
autoAccept: true
|
|
});
|
|
expect(effectiveTaskFlag(undefined, true)).toBe(true);
|
|
expect(effectiveTaskFlag(false, true)).toBe(false);
|
|
const inputs = taskAssignmentInputsFromDrafts(drafts, [
|
|
taskViewModel({ id: "task-1" })
|
|
]);
|
|
expect(inputs).toEqual([
|
|
{
|
|
taskId: "task-1",
|
|
agentTargetId: "agent-2",
|
|
modelPlanId: "",
|
|
model: "",
|
|
permissionModeId: "",
|
|
reasoningEffort: "",
|
|
parallelizable: true,
|
|
autoAccept: true
|
|
}
|
|
]);
|
|
});
|
|
|
|
it("keeps other fields when the same agent is re-selected", () => {
|
|
let drafts = mergeTaskAssignmentDraft({}, "task-1", {
|
|
agentTargetId: "agent-1"
|
|
});
|
|
drafts = mergeTaskAssignmentDraft(drafts, "task-1", { model: "model-9" });
|
|
drafts = mergeTaskAssignmentDraft(drafts, "task-1", {
|
|
agentTargetId: "agent-1"
|
|
});
|
|
expect(drafts["task-1"]?.model).toBe("model-9");
|
|
});
|
|
});
|
|
|
|
describe("effectiveTaskAssignmentValue", () => {
|
|
it("prefers the draft edit, including an explicit clear", () => {
|
|
expect(effectiveTaskAssignmentValue(undefined, "doc-value")).toBe(
|
|
"doc-value"
|
|
);
|
|
expect(effectiveTaskAssignmentValue("edit", "doc-value")).toBe("edit");
|
|
expect(effectiveTaskAssignmentValue("", "doc-value")).toBe("");
|
|
expect(effectiveTaskAssignmentValue(undefined, null)).toBe("");
|
|
});
|
|
});
|
|
|
|
describe("taskAssignmentInputsFromDrafts", () => {
|
|
it("sends only touched tasks and touched fields", () => {
|
|
const tasks = [
|
|
taskViewModel({ id: "task-1" }),
|
|
taskViewModel({ id: "task-2", ordinal: 2 })
|
|
];
|
|
const drafts = mergeTaskAssignmentDraft({}, "task-2", {
|
|
reasoningEffort: "high"
|
|
});
|
|
const inputs = taskAssignmentInputsFromDrafts(drafts, tasks);
|
|
expect(inputs).toEqual([{ taskId: "task-2", reasoningEffort: "high" }]);
|
|
expect("model" in inputs[0]!).toBe(false);
|
|
});
|
|
|
|
it("carries the dependent clears produced by an agent change", () => {
|
|
const tasks = [taskViewModel({ id: "task-1" })];
|
|
const drafts = mergeTaskAssignmentDraft({}, "task-1", {
|
|
agentTargetId: "agent-1"
|
|
});
|
|
expect(taskAssignmentInputsFromDrafts(drafts, tasks)).toEqual([
|
|
{
|
|
taskId: "task-1",
|
|
agentTargetId: "agent-1",
|
|
modelPlanId: "",
|
|
model: "",
|
|
permissionModeId: "",
|
|
reasoningEffort: ""
|
|
}
|
|
]);
|
|
});
|
|
|
|
it("drops drafts for tasks missing from the current revision", () => {
|
|
const drafts = mergeTaskAssignmentDraft({}, "task-gone", {
|
|
model: "model-1"
|
|
});
|
|
expect(
|
|
taskAssignmentInputsFromDrafts(drafts, [taskViewModel({ id: "task-1" })])
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("returns an empty payload for untouched drafts", () => {
|
|
expect(
|
|
taskAssignmentInputsFromDrafts({}, [taskViewModel({ id: "task-1" })])
|
|
).toEqual([]);
|
|
});
|
|
});
|