-
Notifications
You must be signed in to change notification settings - Fork 10.2k
feat(execution): execution_approved wake reason + self-approval fallback #4429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1261,4 +1261,79 @@ describe("issue execution policy transitions", () => { | |
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The existing test suite in Prompt To Fix With AIThis is a comment left during a code review.
Path: server/src/__tests__/issue-execution-policy.test.ts
Line: 1261
Comment:
**Missing test for `shouldResetTaskSessionForWake` with new wake reason**
The existing test suite in `heartbeat-workspace-session.test.ts` has explicit cases for `execution_review_requested`, `execution_approval_requested`, and `execution_changes_requested`, but no case for the newly added `execution_approved` reason. Adding a parallel test there would ensure consistency and guard against regressions if the function is refactored.
How can I resolve this? If you propose a fix, please make it concise. |
||
| }); | ||
| }); | ||
|
|
||
| describe("single-participant stage coinciding with return assignee", () => { | ||
| it("starts workflow by falling back to the sole participant even if it is the return assignee", () => { | ||
| const policy = makePolicy([ | ||
| { type: "approval", participants: [{ type: "agent", agentId: coderAgentId }] }, | ||
| ]); | ||
| const result = applyIssueExecutionPolicyTransition({ | ||
| issue: { | ||
| status: "in_progress", | ||
| assigneeAgentId: coderAgentId, | ||
| assigneeUserId: null, | ||
| executionPolicy: policy, | ||
| executionState: null, | ||
| }, | ||
| policy, | ||
| requestedStatus: "done", | ||
| requestedAssigneePatch: {}, | ||
| actor: { agentId: coderAgentId }, | ||
| commentBody: "Self-approval allowed because policy lists only this participant", | ||
| }); | ||
|
|
||
| expect(result.patch).toMatchObject({ | ||
| status: "in_review", | ||
| assigneeAgentId: coderAgentId, | ||
| executionState: { | ||
| status: "pending", | ||
| currentStageType: "approval", | ||
| currentParticipant: { type: "agent", agentId: coderAgentId }, | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| it("advances to a next stage whose sole participant is the return assignee", () => { | ||
| const policy = makePolicy([ | ||
| { type: "review", participants: [{ type: "agent", agentId: qaAgentId }] }, | ||
| { type: "approval", participants: [{ type: "agent", agentId: coderAgentId }] }, | ||
| ]); | ||
| const reviewStageId = policy.stages[0].id; | ||
| const result = applyIssueExecutionPolicyTransition({ | ||
| issue: { | ||
| status: "in_review", | ||
| assigneeAgentId: qaAgentId, | ||
| assigneeUserId: null, | ||
| executionPolicy: policy, | ||
| executionState: { | ||
| status: "pending", | ||
| currentStageId: reviewStageId, | ||
| currentStageIndex: 0, | ||
| currentStageType: "review", | ||
| currentParticipant: { type: "agent", agentId: qaAgentId }, | ||
| returnAssignee: { type: "agent", agentId: coderAgentId }, | ||
| completedStageIds: [], | ||
| lastDecisionId: null, | ||
| lastDecisionOutcome: null, | ||
| }, | ||
| }, | ||
| policy, | ||
| requestedStatus: "done", | ||
| requestedAssigneePatch: {}, | ||
| actor: { agentId: qaAgentId }, | ||
| commentBody: "QA signoff", | ||
| }); | ||
|
|
||
| expect(result.patch).toMatchObject({ | ||
| status: "in_review", | ||
| assigneeAgentId: coderAgentId, | ||
| executionState: { | ||
| status: "pending", | ||
| currentStageType: "approval", | ||
| currentParticipant: { type: "agent", agentId: coderAgentId }, | ||
| }, | ||
| }); | ||
| expect(result.decision?.outcome).toBe("approved"); | ||
| }); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lastDecisionOutcomealone, not tied to wake reasonThe condition
executionStage.lastDecisionOutcome === "approved"is used to decide which executor prompt to show, but thebuildCompletedStatefunction setslastDecisionOutcome: "approved"as a permanent field even if the state later transitions elsewhere. If an executor receives a non-execution_approvedwake while the last decision happens to be"approved"(e.g. a liveness / comment wake during a completed-but-not-yet-acknowledged window), they will see the completion prompt instead of a general-purpose one.Tying this branch to a wake-reason check (
executionStage.wakeRole === "executor" && wakeReason === "execution_approved") — or including the wake reason in theExecutionStageWakeContextpassed to this renderer — would make the condition explicit and safe.Prompt To Fix With AI