Distinguish top-level review wakes from subtask completion

Top-level tasks submitted to in_review fired the same
subtask_completed wake as the subtree leaves, so the reviewer's
prompt framed the work as "a subtask you delegated". Add a dedicated
task_review_requested wake reason with a reviewer-specific title and
step list, and route the parent-less branch of the task PATCH handler
through it.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-19 19:26:05 -04:00
1 parent 8bf480236f
commit 2e60ecc0e0
4 files changed
+21 -3

No files matched your search

+5 -1
View File
@@ -229,9 +229,13 @@ export function registerTaskRoutes(server: AgentApiServer) {
);
}
} else {
// Top-level task submission goes to the assignee's manager.
// This is not a subtask — use a distinct wake reason so the
// reviewer prompt does not misframe the work as "a subtask
// you delegated".
const chain = db.getChainOfCommand(token.agentId);
if (chain.length > 0) {
onWakeAgent(chain[0].id, task.companyId, "subtask_completed");
onWakeAgent(chain[0].id, task.companyId, "task_review_requested");
}
}
}
@@ -87,6 +87,8 @@ function buildGenericWakeTurn(trigger: string | null): string {
return "An approval you requested has been decided. Act on the outcome surfaced in your briefing.";
case "subtask_completed":
return "A subtask you delegated is ready for review. Evaluate the work against its acceptance criteria and either accept it or request changes.";
case "task_review_requested":
return "A direct report submitted a top-level task for your review. Validate against the original acceptance criteria before deciding.";
case "goal_activated":
return "A goal you own has been activated. Decompose it into projects and tasks, then delegate the first batch of work.";
case "report_blocked":
+13 -1
View File
@@ -5,6 +5,7 @@ export type WakeReason =
| "goal_activated"
| "approval_resolved"
| "subtask_completed"
| "task_review_requested"
| "comment"
| "report_blocked"
| "report_failed"
@@ -69,7 +70,7 @@ export const WAKE_REASONS: Record<WakeReason, WakeReasonEntry> = {
subtask_completed: {
title: "A subtask you delegated has been completed or moved to review.",
steps: () => [
'Check tasks with status "in_review" from your reports',
'Check tasks with status "in_review" whose parent task you own',
"Read ALL comments on the completed subtask (GET /api/issues/{issueId}/comments)",
"Review the work quality against the acceptance criteria you set",
'If acceptable: PATCH /api/issues/{issueId} with status "done" and an approving comment',
@@ -77,6 +78,17 @@ export const WAKE_REASONS: Record<WakeReason, WakeReasonEntry> = {
"Check if the parent task can now progress",
],
},
task_review_requested: {
title: "A direct report submitted a top-level task for your review.",
steps: () => [
'Find the task: GET /api/agents/me/standup — look for tasksAwaitingReview',
"Read ALL comments on the task (GET /api/issues/{issueId}/comments) and any linked documents",
"Validate the work against the original acceptance criteria — not just whether it looks complete",
'If acceptable: PATCH /api/issues/{issueId} with status "done" and a concrete approval comment',
'If changes needed: PATCH /api/issues/{issueId} with status "in_progress" and specific, actionable feedback',
"Do not let a top-level review sit — your whole team is downstream",
],
},
comment: {
title: "A new comment has been posted on one of your tasks.",
steps: () => [
+1 -1
View File
@@ -49,5 +49,5 @@ export const heartbeatSettingsSchema = z.object({
export const triggerHeartbeatSchema = z.object({
agentId: z.string().uuid(),
companyId: z.string().uuid(),
trigger: z.enum(["manual", "timer", "assignment", "goal_activated", "approval_resolved", "subtask_completed", "comment", "report_blocked", "report_failed", "recovery", "message", "peer_request", "task_continuation"]).default("manual"),
trigger: z.enum(["manual", "timer", "assignment", "goal_activated", "approval_resolved", "subtask_completed", "task_review_requested", "comment", "report_blocked", "report_failed", "recovery", "message", "peer_request", "task_continuation"]).default("manual"),
});