mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-16 15:15:35 +02:00
fix: include REST creation recipients in composer assignment history
This commit is contained in:
1 parent
c41b6bcc12
commit
5a7d5e98a8
5 files changed
+42
-4
No files matched your search
@@ -78,6 +78,7 @@ describeEmbeddedPostgres("issue comment attribution and patch audit routes", ()
|
||||
async function seed() {
|
||||
const company = await db.insert(companies).values({
|
||||
name: "Attribution Company",
|
||||
issueCounter: 2,
|
||||
issuePrefix: `AT${randomUUID().replaceAll("-", "").slice(0, 6).toUpperCase()}`,
|
||||
}).returning().then((rows) => rows[0]!);
|
||||
const [actorAgent, targetAgent] = await db.insert(agents).values(["Actor", "Target"].map((name) => ({
|
||||
@@ -162,6 +163,27 @@ describeEmbeddedPostgres("issue comment attribution and patch audit routes", ()
|
||||
};
|
||||
}
|
||||
|
||||
it("records the human recipient for REST task and child creation", async () => {
|
||||
const fixture = await seed();
|
||||
const app = await createApp(db, agentActor(fixture));
|
||||
for (const [path, action] of [
|
||||
[`/api/companies/${fixture.company.id}/issues`, "issue.created"],
|
||||
[`/api/issues/${fixture.issue.id}/children`, "issue.child_created"],
|
||||
]) {
|
||||
const response = await request(app).post(path!).send({
|
||||
title: "Task for the human", status: "todo", assigneeUserId: fixture.responsibleUserId,
|
||||
});
|
||||
expect(response.status, JSON.stringify(response.body)).toBe(201);
|
||||
const events = await db.select().from(activityLog).where(and(
|
||||
eq(activityLog.entityId, response.body.id), eq(activityLog.action, action!),
|
||||
));
|
||||
expect(events).toEqual([expect.objectContaining({
|
||||
actorType: "agent", actorId: fixture.actorAgent.id,
|
||||
details: expect.objectContaining({ assigneeAgentId: null, assigneeUserId: fixture.responsibleUserId }),
|
||||
})]);
|
||||
}
|
||||
});
|
||||
|
||||
it("attributes foreign-issue agent comments and emits complete agent and human PATCH receipts", async () => {
|
||||
const fixture = await seed();
|
||||
const agentApp = await createApp(db, agentActor(fixture));
|
||||
|
||||
@@ -653,7 +653,7 @@ const attachmentArtifactMetadataInputSchema = z
|
||||
.passthrough();
|
||||
|
||||
function buildCreateIssueActivityStatusDetails(
|
||||
issue: { assigneeAgentId: string | null; status: string },
|
||||
issue: { assigneeAgentId: string | null; assigneeUserId: string | null; status: string },
|
||||
res: Response,
|
||||
) {
|
||||
const statusDefault = res.locals.createIssueStatusDefault as
|
||||
@@ -661,6 +661,8 @@ function buildCreateIssueActivityStatusDetails(
|
||||
const assignmentWakeSkipped =
|
||||
!issue.assigneeAgentId || issue.status === "backlog";
|
||||
return {
|
||||
assigneeAgentId: issue.assigneeAgentId,
|
||||
assigneeUserId: issue.assigneeUserId,
|
||||
status: issue.status,
|
||||
statusDefaulted: statusDefault?.defaulted ?? false,
|
||||
statusDefaultReason: statusDefault?.reason ?? "explicit",
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
// Real local Board UI, upload storage, comment HTTP routes, and disposable DB.
|
||||
// Only the interface flag and explicit transport/upload rejection faults are mocked.
|
||||
// Tasks belong to the Board user: no agent, runner, or provider is contacted.
|
||||
// Tasks have no recipient: no agent, runner, or provider is contacted.
|
||||
type Attachment = {
|
||||
id: string;
|
||||
issueCommentId: string | null;
|
||||
@@ -44,7 +44,9 @@ async function setup(page: Page, request: APIRequestContext, classic: boolean) {
|
||||
data: {
|
||||
title: "Inspect the newly uploaded Board files",
|
||||
status: "todo",
|
||||
assigneeUserId: "local-board",
|
||||
// Keep attachment sends on the comment route. A task assigned to the
|
||||
// current human now suggests an empty recipient and sends a reassignment.
|
||||
assigneeUserId: null,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -154,3 +154,15 @@ it("prefers a newer native reassignment over a REST assignment and ignores nativ
|
||||
];
|
||||
expect(suggestedCommentAssigneeValue({ assigneeUserId: "me" }, [], "me", undefined, history)).toBe("agent:beta");
|
||||
});
|
||||
|
||||
|
||||
it.each(["issue.created", "issue.child_created"])("uses a REST %s receipt assigned to the current human", (action) => {
|
||||
const event: ActivityEvent = {
|
||||
id: "creation", companyId: "company", entityType: "issue", entityId: "task",
|
||||
agentId: "creator", runId: null, actorType: "agent", actorId: "creator", action,
|
||||
createdAt: new Date("2026-02-01"),
|
||||
details: { title: "Task", identifier: "PAP-1", status: "todo", assigneeAgentId: null, assigneeUserId: "me" },
|
||||
};
|
||||
expect(suggestedCommentAssigneeValue({ assigneeUserId: "me" }, [{ authorAgentId: "commenter" }], "me", undefined, [event])).toBe("agent:creator");
|
||||
expect(suggestedCommentAssigneeValue({ assigneeUserId: "other" }, [], "other", undefined, [event])).toBe("");
|
||||
});
|
||||
@@ -41,7 +41,7 @@ export function suggestedCommentAssigneeValue(
|
||||
if (event.actorType !== "agent" || !event.actorId || event.actorId === currentAgentId) return false;
|
||||
const details = event.details;
|
||||
if (!details || details.assigneeUserId !== currentUserId) return false;
|
||||
if (event.action === "issue.created") return true;
|
||||
if (event.action === "issue.created" || event.action === "issue.child_created") return true;
|
||||
if (event.action === "issue.reassigned") {
|
||||
return details.changed === true
|
||||
&& Object.hasOwn(details, "previousAssigneeUserId")
|
||||
|
||||
Reference in new issue
Block a user