mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 18:30:39 +09:00
Add draft routine defaults and run-time overrides
This commit is contained in:
parent
b4a58ba8a6
commit
5d021583be
18 changed files with 592 additions and 113 deletions
|
|
@ -329,6 +329,53 @@ describeEmbeddedPostgres("routine routes end-to-end", () => {
|
|||
expect(issue?.description).toBe("Review paperclip for high bugs");
|
||||
});
|
||||
|
||||
it("allows drafting a routine without defaults and running it with one-off overrides", async () => {
|
||||
const { companyId, agentId, projectId, userId } = await seedFixture();
|
||||
const app = await createApp({
|
||||
type: "board",
|
||||
userId,
|
||||
source: "session",
|
||||
isInstanceAdmin: false,
|
||||
companyIds: [companyId],
|
||||
});
|
||||
|
||||
const createRes = await request(app)
|
||||
.post(`/api/companies/${companyId}/routines`)
|
||||
.send({
|
||||
title: "Draft routine",
|
||||
description: "No saved defaults",
|
||||
});
|
||||
|
||||
expect(createRes.status).toBe(201);
|
||||
expect(createRes.body.projectId).toBeNull();
|
||||
expect(createRes.body.assigneeAgentId).toBeNull();
|
||||
expect(createRes.body.status).toBe("paused");
|
||||
|
||||
const runRes = await request(app)
|
||||
.post(`/api/routines/${createRes.body.id}/run`)
|
||||
.send({
|
||||
source: "manual",
|
||||
projectId,
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
|
||||
expect(runRes.status).toBe(202);
|
||||
expect(runRes.body.status).toBe("issue_created");
|
||||
|
||||
const [issue] = await db
|
||||
.select({
|
||||
projectId: issues.projectId,
|
||||
assigneeAgentId: issues.assigneeAgentId,
|
||||
})
|
||||
.from(issues)
|
||||
.where(eq(issues.id, runRes.body.linkedIssueId));
|
||||
|
||||
expect(issue).toEqual({
|
||||
projectId,
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
});
|
||||
|
||||
it("persists execution workspace selections from manual routine runs", async () => {
|
||||
const { companyId, agentId, projectId, userId } = await seedFixture();
|
||||
const projectWorkspaceId = randomUUID();
|
||||
|
|
|
|||
|
|
@ -221,6 +221,31 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => {
|
|||
expect(routineIssues.map((issue) => issue.id)).toContain(run.linkedIssueId);
|
||||
});
|
||||
|
||||
it("creates draft routines without a project or default assignee", async () => {
|
||||
const { companyId, svc } = await seedFixture();
|
||||
|
||||
const routine = await svc.create(
|
||||
companyId,
|
||||
{
|
||||
projectId: null,
|
||||
goalId: null,
|
||||
parentIssueId: null,
|
||||
title: "draft routine",
|
||||
description: "No defaults yet",
|
||||
assigneeAgentId: null,
|
||||
priority: "medium",
|
||||
status: "active",
|
||||
concurrencyPolicy: "coalesce_if_active",
|
||||
catchUpPolicy: "skip_missed",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
expect(routine.projectId).toBeNull();
|
||||
expect(routine.assigneeAgentId).toBeNull();
|
||||
expect(routine.status).toBe("paused");
|
||||
});
|
||||
|
||||
it("wakes the assignee when a routine creates a fresh execution issue", async () => {
|
||||
const { agentId, routine, svc, wakeups } = await seedFixture();
|
||||
|
||||
|
|
@ -436,6 +461,73 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("runs draft routines with one-off agent and project overrides", async () => {
|
||||
const { companyId, agentId, projectId, svc } = await seedFixture();
|
||||
const draftRoutine = await svc.create(
|
||||
companyId,
|
||||
{
|
||||
projectId: null,
|
||||
goalId: null,
|
||||
parentIssueId: null,
|
||||
title: "draft dispatch",
|
||||
description: "Pick defaults at run time",
|
||||
assigneeAgentId: null,
|
||||
priority: "medium",
|
||||
status: "paused",
|
||||
concurrencyPolicy: "coalesce_if_active",
|
||||
catchUpPolicy: "skip_missed",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const run = await svc.runRoutine(draftRoutine.id, {
|
||||
source: "manual",
|
||||
projectId,
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
|
||||
expect(run.status).toBe("issue_created");
|
||||
expect(run.linkedIssueId).toBeTruthy();
|
||||
|
||||
const storedIssue = await db
|
||||
.select({
|
||||
projectId: issues.projectId,
|
||||
assigneeAgentId: issues.assigneeAgentId,
|
||||
})
|
||||
.from(issues)
|
||||
.where(eq(issues.id, run.linkedIssueId!))
|
||||
.then((rows) => rows[0] ?? null);
|
||||
|
||||
expect(storedIssue).toEqual({
|
||||
projectId,
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects enabling automation for routines without a default agent", async () => {
|
||||
const { companyId, svc } = await seedFixture();
|
||||
const draftRoutine = await svc.create(
|
||||
companyId,
|
||||
{
|
||||
projectId: null,
|
||||
goalId: null,
|
||||
parentIssueId: null,
|
||||
title: "draft routine",
|
||||
description: null,
|
||||
assigneeAgentId: null,
|
||||
priority: "medium",
|
||||
status: "paused",
|
||||
concurrencyPolicy: "coalesce_if_active",
|
||||
catchUpPolicy: "skip_missed",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
await expect(
|
||||
svc.update(draftRoutine.id, { status: "active" }, {}),
|
||||
).rejects.toThrow(/default agent required/i);
|
||||
});
|
||||
|
||||
it("blocks schedule triggers when required variables do not have defaults", async () => {
|
||||
const { companyId, agentId, projectId, svc } = await seedFixture();
|
||||
const variableRoutine = await svc.create(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue