engineering-memory 1.11.28 → 1.11.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ const option = z.strictObject({
7
7
  label: z.string().trim().min(1).max(100),
8
8
  description: z.string().trim().min(1).max(240),
9
9
  });
10
+ const reviewLine = z.string().trim().min(1).max(300);
10
11
  const copySchema = z.strictObject({
11
12
  message: z.string().trim().min(1).max(240),
12
13
  context: z.string().trim().min(1).max(500),
@@ -17,12 +18,26 @@ const copySchema = z.strictObject({
17
18
  commit_push_draft_pr: option,
18
19
  commit_push_pr: option,
19
20
  defer: option,
21
+ commit_push_review_pr: option.optional(),
22
+ commit_push_pr_unreviewed: option.optional(),
23
+ reviewRequired: reviewLine.optional(),
24
+ reviewOptional: reviewLine.optional(),
25
+ reviewFix: reviewLine.optional(),
20
26
  baseMessage: z.string().trim().min(1).max(240),
21
27
  baseContext: z.string().trim().min(1).max(500),
22
28
  baseTitle: z.string().trim().min(1).max(100),
23
29
  baseWrite: option,
24
30
  recorded: z.string().trim().min(1).max(100).optional(),
25
31
  });
32
+ const deliveryChoices = [
33
+ 'commit',
34
+ 'commit_push',
35
+ 'commit_push_review_pr',
36
+ 'commit_push_draft_pr',
37
+ 'commit_push_pr',
38
+ 'defer',
39
+ ];
40
+ const reviewPolicy = z.enum(['none', 'optional', 'required']);
26
41
  const closedSchema = z.object({
27
42
  closed: z.literal(true),
28
43
  taskVersion: z.number().int().positive(),
@@ -47,8 +62,11 @@ const closedSchema = z.object({
47
62
  taskId: z.string(),
48
63
  lockVersion: z.number().int(),
49
64
  choice: z.enum(['commit', 'commit_push', 'commit_push_draft_pr', 'commit_push_pr']).nullish(),
65
+ reviewState: z.string().nullish(),
50
66
  })
51
67
  .nullish(),
68
+ reviewPolicy: reviewPolicy.optional(),
69
+ reviewFixOf: z.object({ taskId: z.string(), externalTaskId: z.string() }).nullish(),
52
70
  });
53
71
  const recordWording = z.strictObject({
54
72
  message: z.string().trim().min(1).max(240),
@@ -63,7 +81,48 @@ const recordSchema = z.object({
63
81
  branch: z.string().nullable(),
64
82
  state: z.enum(['unanswered', 'answered', 'delivered', 'cancelled']),
65
83
  lockVersion: z.number().int(),
84
+ reviewPolicy: reviewPolicy.optional(),
85
+ reviewFixOfDeliveryId: z.string().nullish(),
66
86
  });
87
+ function offered(policy, fix) {
88
+ if (fix)
89
+ return ['commit', 'commit_push'];
90
+ if (policy === 'required')
91
+ return ['commit', 'commit_push', 'commit_push_review_pr'];
92
+ if (policy === 'optional')
93
+ return ['commit', 'commit_push', 'commit_push_review_pr', 'commit_push_pr'];
94
+ return ['commit', 'commit_push', 'commit_push_draft_pr', 'commit_push_pr'];
95
+ }
96
+ function wording(copy, policy, choice) {
97
+ return choice === 'commit_push_pr' && policy === 'optional'
98
+ ? copy.commit_push_pr_unreviewed
99
+ : copy[choice];
100
+ }
101
+ function pullRequest(choice) {
102
+ return (choice === 'commit_push_review_pr' ||
103
+ choice === 'commit_push_draft_pr' ||
104
+ choice === 'commit_push_pr');
105
+ }
106
+ const incompleteCopy = {
107
+ ok: false,
108
+ error: {
109
+ kind: 'delivery',
110
+ message: "Supply complete delivery copy in the conversation language before closing, including the code review wording this project's question needs (commit_push_review_pr, commit_push_pr_unreviewed, reviewRequired, reviewOptional, reviewFix).",
111
+ recovery: 'task.close',
112
+ retryable: false,
113
+ },
114
+ };
115
+ function settingChanged(tool, now) {
116
+ return output({
117
+ ok: false,
118
+ error: {
119
+ kind: 'delivery',
120
+ message: `The project's code review setting changed to ${now} while the delivery question was open, so the answer was not recorded and no Git action was performed. Tell the user, then call ${tool} again: its question follows the new setting.`,
121
+ recovery: tool,
122
+ retryable: true,
123
+ },
124
+ });
125
+ }
67
126
  export function registerDeliveryTools(server, service) {
68
127
  registerTaskDelivery(server, service);
69
128
  registerTaskReviewRequest(server, service);
@@ -79,13 +138,7 @@ export function registerDeliveryTools(server, service) {
79
138
  decisionAttempt: z.number().int().min(1).max(10000).optional(),
80
139
  deliveryInstruction: z
81
140
  .strictObject({
82
- choice: z.enum([
83
- 'commit',
84
- 'commit_push',
85
- 'commit_push_draft_pr',
86
- 'commit_push_pr',
87
- 'defer',
88
- ]),
141
+ choice: z.enum(deliveryChoices),
89
142
  userRequestExcerpt: z.string().trim().min(1).max(1200),
90
143
  baseBranch: z.string().trim().min(1).max(240).optional(),
91
144
  })
@@ -123,6 +176,8 @@ export function registerDeliveryTools(server, service) {
123
176
  const data = parsed.data;
124
177
  if (data.deliveryContext.mode === 'read_only')
125
178
  return output(closed);
179
+ const policy = data.reviewPolicy ?? 'none';
180
+ const fix = data.reviewFixOf ?? null;
126
181
  const delivered = data.delivery.alreadyDelivered;
127
182
  if (delivered)
128
183
  return output({
@@ -130,7 +185,12 @@ export function registerDeliveryTools(server, service) {
130
185
  data: {
131
186
  ...closed.data,
132
187
  deliveryStatus: 'delivered',
133
- nextAction: `Everything this task changed was already committed and pushed by hand: ${delivered.ref.replace(/^refs\/remotes\//, '')} contains ${delivered.commit.slice(0, 12)}. There is nothing left to deliver, so no delivery question is asked, and the delivery is recorded for the project. Call worktree.release in this folder with deliveryOutcome delivered to settle the folder.`,
188
+ nextAction: `Everything this task changed was already committed and pushed by hand: ${delivered.ref.replace(/^refs\/remotes\//, '')} contains ${delivered.commit.slice(0, 12)}. There is nothing left to deliver, so no delivery question is asked, and the delivery is recorded for the project. Call worktree.release in this folder with deliveryOutcome delivered to settle the folder.` +
189
+ (fix
190
+ ? ` This task fixes the review findings of ${fix.externalTaskId}: once the push is recorded, its open findings are marked fixed at this commit. Read review.get for ${fix.externalTaskId} and ask the user whether to review it again (review.start) or end its review as passed (review.conclude).`
191
+ : policy === 'required'
192
+ ? ' Code review is required in this project: if a PR/MR is opened for this branch, open it as a draft and record it (task.open_review_request on GitLab, task.review_request elsewhere). Recording it starts the review, which review.start runs.'
193
+ : ''),
134
194
  },
135
195
  });
136
196
  const destination = data.delivery.destination;
@@ -152,6 +212,8 @@ export function registerDeliveryTools(server, service) {
152
212
  language,
153
213
  copy,
154
214
  decisionAttempt: input.decisionAttempt ?? 0,
215
+ reviewPolicy: policy,
216
+ reviewFixOf: fix?.taskId ?? null,
155
217
  };
156
218
  const binding = { deliveryDigest: sha256(stableStringify(identity)) };
157
219
  const owner = {
@@ -163,20 +225,47 @@ export function registerDeliveryTools(server, service) {
163
225
  const questionnaireId = 'task-delivery-' + binding.deliveryDigest;
164
226
  const canPush = Boolean(destination.remote && destination.pushUrl);
165
227
  const ids = canPush
166
- ? ['commit', 'commit_push', 'commit_push_draft_pr', 'commit_push_pr', 'defer']
228
+ ? [...offered(policy, Boolean(fix)), 'defer']
167
229
  : ['commit', 'defer'];
168
230
  const instruction = input.deliveryInstruction;
169
- const recorded = data.deliveryRecord?.choice;
170
- if (instruction && !ids.some((id) => id === instruction.choice))
231
+ const instructed = !instruction
232
+ ? null
233
+ : fix && pullRequest(instruction.choice)
234
+ ? 'commit_push'
235
+ : policy === 'required' && pullRequest(instruction.choice)
236
+ ? 'commit_push_review_pr'
237
+ : instruction.choice;
238
+ if (instructed && !ids.includes(instructed))
171
239
  return output({
172
240
  ok: false,
173
241
  error: {
174
242
  kind: 'delivery',
175
- message: 'This delivery needs an available remote and push URL. No Git action was performed.',
243
+ message: !canPush
244
+ ? 'This delivery needs an available remote and push URL. No Git action was performed.'
245
+ : policy === 'none'
246
+ ? 'Code review is off in this project, so a reviewed PR/MR is not offered. No Git action was performed. Call task.close without deliveryInstruction to ask the user how to deliver.'
247
+ : 'Code review is optional in this project, so a draft PR/MR is either reviewed first or opened without review, and the instruction does not say which. No Git action was performed. Call task.close without deliveryInstruction to ask the user.',
176
248
  recovery: 'task.close',
177
249
  retryable: false,
178
250
  },
179
251
  });
252
+ const line = !canPush
253
+ ? ''
254
+ : fix
255
+ ? copy.reviewFix && format(copy.reviewFix, language ?? 'en', { task: fix.externalTaskId })
256
+ : policy === 'required'
257
+ ? copy.reviewRequired
258
+ : policy === 'optional'
259
+ ? copy.reviewOptional
260
+ : '';
261
+ if (!instruction && (line === undefined || ids.some((id) => !wording(copy, policy, id))))
262
+ return output(incompleteCopy);
263
+ const recorded = data.deliveryRecord?.choice;
264
+ const recordedLabel = !recorded
265
+ ? undefined
266
+ : data.deliveryRecord?.reviewState === 'pending'
267
+ ? (copy.commit_push_review_pr ?? copy[recorded]).label
268
+ : (wording(copy, policy, recorded) ?? copy[recorded]).label;
180
269
  const form = instruction
181
270
  ? null
182
271
  : await askQuestionnaire(server, service, {
@@ -192,18 +281,15 @@ export function registerDeliveryTools(server, service) {
192
281
  ': ' +
193
282
  destination.branch +
194
283
  (canPush ? ' → ' + destination.remote + ' (' + destination.pushUrl + ')' : '') +
195
- (recorded && copy.recorded
196
- ? '\n' + copy.recorded + ': ' + copy[recorded].label
197
- : ''),
284
+ (recordedLabel && copy.recorded
285
+ ? '\n' + copy.recorded + ': ' + recordedLabel
286
+ : '') +
287
+ (line ? '\n' + line : ''),
198
288
  example: copy.example,
199
- options: ids.map((id) => ({
200
- id,
201
- label: copy[id].label,
202
- description: copy[id].description,
203
- })),
289
+ options: ids.map((id) => ({ id, ...wording(copy, policy, id) })),
204
290
  binding,
205
291
  }, context, [], owner);
206
- const choice = instruction?.choice ?? (form ? answerChoice(form) : null);
292
+ const choice = instructed ?? (form ? answerChoice(form) : null);
207
293
  if (!choice)
208
294
  return form;
209
295
  const { deliveryInstruction: _instruction, ...formInput } = input;
@@ -227,11 +313,9 @@ export function registerDeliveryTools(server, service) {
227
313
  return deferred();
228
314
  service.live.deliveryStarted();
229
315
  try {
230
- let baseBranch = choice === 'commit_push_draft_pr' || choice === 'commit_push_pr'
231
- ? instruction?.baseBranch
232
- : undefined;
316
+ let baseBranch = pullRequest(choice) ? instruction?.baseBranch : undefined;
233
317
  let baseQuestionnaireId;
234
- if ((choice === 'commit_push_draft_pr' || choice === 'commit_push_pr') && !baseBranch) {
318
+ if (pullRequest(choice) && !baseBranch) {
235
319
  baseQuestionnaireId = 'task-delivery-base-' + sha256(questionnaireId + choice);
236
320
  const base = await askPullRequestBase(server, service, context, { ...input, questionnaireId: baseQuestionnaireId, binding: { ...binding, choice } }, language, copy, owner);
237
321
  if (!answerChoice(base))
@@ -242,7 +326,7 @@ export function registerDeliveryTools(server, service) {
242
326
  if (!baseBranch)
243
327
  return base;
244
328
  }
245
- if (choice === 'commit_push_draft_pr' || choice === 'commit_push_pr') {
329
+ if (pullRequest(choice)) {
246
330
  const valid = await service.taskDeliveryBase({
247
331
  repoRoot: input.repoRoot,
248
332
  branch: baseBranch,
@@ -299,6 +383,9 @@ export function registerDeliveryTools(server, service) {
299
383
  retryable: false,
300
384
  },
301
385
  });
386
+ const now = current.data.reviewPolicy ?? 'none';
387
+ if (pullRequest(choice) && now !== policy)
388
+ return settingChanged('task.close', now);
302
389
  const question = instruction
303
390
  ? null
304
391
  : await service.questionnaireResume({ repoRoot: input.repoRoot, questionnaireId });
@@ -313,14 +400,21 @@ export function registerDeliveryTools(server, service) {
313
400
  projectId: record.projectId,
314
401
  taskId: record.taskId,
315
402
  expectedVersion: record.lockVersion,
316
- choice,
403
+ choice: choice === 'commit_push_review_pr' ? 'commit_push_draft_pr' : choice,
317
404
  ...(baseBranch ? { baseBranch } : {}),
405
+ ...(pullRequest(choice) && data.reviewPolicy
406
+ ? { expectedReviewPolicy: data.reviewPolicy }
407
+ : {}),
318
408
  answerSource: question ? answerSourceName(question.answerSource) : 'user_instruction',
319
409
  })
320
410
  : null;
321
411
  const answered = answer?.ok ? answer.data : null;
322
412
  const refused = answer && !answer.ok ? answer.error : null;
323
- const other = refused?.details?.delivery;
413
+ const details = refused?.details;
414
+ if (details?.reviewPolicy)
415
+ return settingChanged('task.close', details.reviewPolicy);
416
+ const other = details?.delivery;
417
+ const told = instruction && instructed !== instruction.choice ? instruction.choice : null;
324
418
  return output({
325
419
  ok: true,
326
420
  data: {
@@ -352,7 +446,24 @@ export function registerDeliveryTools(server, service) {
352
446
  ...(baseQuestionnaireId ? { baseQuestionnaireId } : {}),
353
447
  destination,
354
448
  },
355
- nextAction: 'Perform only the selected Git delivery under host permissions. No commit, push, PR/MR or merge was executed by task.close. Preserve this exact branch and destination.',
449
+ nextAction: [
450
+ 'Perform only the selected Git delivery under host permissions. No commit, push, PR/MR or merge was executed by task.close. Preserve this exact branch and destination.',
451
+ told && fix
452
+ ? `The user asked for a PR/MR, but this task fixes the review findings of ${fix.externalTaskId}, whose PR/MR is already open: its changes are committed and pushed onto that branch and no new PR/MR is opened. Tell the user.`
453
+ : told
454
+ ? 'The user asked for a PR/MR without saying it is reviewed, and code review is required in this project, so it opens as a draft and becomes ready once the review ends. Tell the user.'
455
+ : '',
456
+ choice === 'commit_push_review_pr'
457
+ ? `After the push and worktree.release, open the PR/MR into ${baseBranch} as a draft: on GitLab task.open_review_request opens it as a draft; elsewhere open it as a draft with your own tools and record it with task.review_request. Then start the code review with review.start for this task. The PR/MR becomes ready when the review ends.`
458
+ : '',
459
+ fix && choice === 'commit_push'
460
+ ? `The push goes onto ${destination.branch}, the open PR/MR branch of ${fix.externalTaskId}; open no new PR/MR. Once the push is recorded by worktree.release, the open findings of ${fix.externalTaskId} are marked fixed at that commit: read review.get for ${fix.externalTaskId} and ask the user whether to review it again (review.start) or end its review as passed (review.conclude).`
461
+ : fix
462
+ ? `The findings of ${fix.externalTaskId} stay open until this commit is pushed onto its PR/MR branch.`
463
+ : '',
464
+ ]
465
+ .filter(Boolean)
466
+ .join(' '),
356
467
  },
357
468
  });
358
469
  }
@@ -375,10 +486,10 @@ function registerTaskDelivery(server, service) {
375
486
  }, async (input, context) => {
376
487
  const language = await service.language(input.language);
377
488
  const copy = language ? copySchema.safeParse(texts('delivery', language)).data : undefined;
378
- const wording = language
489
+ const recordCopy = language
379
490
  ? recordWording.safeParse(texts('taskDelivery', language)).data
380
491
  : undefined;
381
- if (!language || !copy || !wording)
492
+ if (!language || !copy || !recordCopy)
382
493
  return output({
383
494
  ok: false,
384
495
  error: {
@@ -410,7 +521,14 @@ function registerTaskDelivery(server, service) {
410
521
  nextAction: 'This delivery is already concluded. Tell the user what was recorded.',
411
522
  },
412
523
  });
413
- const binding = { taskId: record.taskId, lockVersion: record.lockVersion };
524
+ const policy = record.reviewPolicy ?? 'none';
525
+ const fix = Boolean(record.reviewFixOfDeliveryId);
526
+ const binding = {
527
+ taskId: record.taskId,
528
+ lockVersion: record.lockVersion,
529
+ reviewPolicy: policy,
530
+ reviewFix: fix,
531
+ };
414
532
  const questionnaireId = 'task-delivery-record-' +
415
533
  sha256(stableStringify({ ...binding, decisionAttempt: input.decisionAttempt ?? 0 }));
416
534
  const owner = {
@@ -423,28 +541,38 @@ function registerTaskDelivery(server, service) {
423
541
  language: input.language,
424
542
  },
425
543
  };
426
- const ids = [
427
- 'commit',
428
- 'commit_push',
429
- 'commit_push_draft_pr',
430
- 'commit_push_pr',
431
- 'abandon',
432
- 'defer',
433
- ];
544
+ const ids = offered(policy, fix);
545
+ const line = fix || policy === 'none'
546
+ ? ''
547
+ : policy === 'required'
548
+ ? copy.reviewRequired
549
+ : copy.reviewOptional;
550
+ if (line === undefined || ids.some((id) => !wording(copy, policy, id)))
551
+ return output({
552
+ ok: false,
553
+ error: {
554
+ kind: 'delivery',
555
+ message: "The delivery question has no code review text in the conversation language, and this project's question needs it. Ask again in a language Engineering Memory offers.",
556
+ recovery: 'task.delivery',
557
+ retryable: false,
558
+ },
559
+ });
434
560
  const form = await askQuestionnaire(server, service, {
435
561
  repoRoot: input.repoRoot,
436
562
  presentation: input.presentation,
437
563
  questionnaireId,
438
564
  language,
439
565
  impact: 'critical',
440
- message: format(wording.message, language, { task: record.externalTaskId }),
441
- context: wording.context + (record.branch ? '\n' + copy.destination + ': ' + record.branch : ''),
442
- example: wording.example,
443
- options: ids.map((id) => ({
444
- id,
445
- label: id === 'abandon' ? wording.abandon.label : copy[id].label,
446
- description: id === 'abandon' ? wording.abandon.description : copy[id].description,
447
- })),
566
+ message: format(recordCopy.message, language, { task: record.externalTaskId }),
567
+ context: recordCopy.context +
568
+ (record.branch ? '\n' + copy.destination + ': ' + record.branch : '') +
569
+ (line ? '\n' + line : ''),
570
+ example: recordCopy.example,
571
+ options: [
572
+ ...ids.map((id) => ({ id, ...wording(copy, policy, id) })),
573
+ { id: 'abandon', ...recordCopy.abandon },
574
+ { id: 'defer', ...copy.defer },
575
+ ],
448
576
  binding,
449
577
  }, context, [], owner);
450
578
  const choice = answerChoice(form);
@@ -466,7 +594,7 @@ function registerTaskDelivery(server, service) {
466
594
  reason: 'abandoned',
467
595
  }));
468
596
  let baseBranch;
469
- if (choice === 'commit_push_draft_pr' || choice === 'commit_push_pr') {
597
+ if (pullRequest(choice)) {
470
598
  const base = await askPullRequestBase(server, service, context, {
471
599
  ...input,
472
600
  questionnaireId: 'task-delivery-base-' + sha256(questionnaireId + choice),
@@ -495,10 +623,17 @@ function registerTaskDelivery(server, service) {
495
623
  projectId: input.projectId,
496
624
  taskId: input.taskId,
497
625
  expectedVersion: record.lockVersion,
498
- choice,
626
+ choice: choice === 'commit_push_review_pr' ? 'commit_push_draft_pr' : choice,
499
627
  ...(baseBranch ? { baseBranch } : {}),
628
+ ...(pullRequest(choice) && record.reviewPolicy
629
+ ? { expectedReviewPolicy: record.reviewPolicy }
630
+ : {}),
500
631
  answerSource: answerSourceName(question.answerSource),
501
632
  });
633
+ const changed = answer.error?.details
634
+ ?.reviewPolicy;
635
+ if (changed)
636
+ return settingChanged('task.delivery', changed);
502
637
  if (!answer.ok)
503
638
  return output(answer);
504
639
  return output({
@@ -512,28 +647,47 @@ function registerTaskDelivery(server, service) {
512
647
  }
513
648
  function registerTaskReviewRequest(server, service) {
514
649
  server.registerTool('task.review_request', {
515
- description: "Record the pull request (GitHub) or merge request (GitLab) a closed task's changes went into, and its state: draft, ready, merged or closed. Call it with the link copied from the provider as soon as the pull request is opened, and again when the user says it became ready, was merged or was closed. Engineering Memory then moves the task's work item as the project's workflow rules say, for example to PR when every pull request of the work item is ready and to READY FOR QA when they are all merged. It works from any chat, also for a delivery session.entry lists. What is recorded is what was reported: Engineering Memory does not check it with the provider, and performs no Git or provider action.",
650
+ description: "Record the pull request (GitHub) or merge request (GitLab) a closed task's changes went into, and its state: draft, ready, merged or closed. Call it with the link copied from the provider as soon as the pull request is opened, and again when the user says it became ready, was merged or was closed. Pass headCommit and targetBranch when you can read them from the provider (on GitHub, gh pr view with --json headRefOid,baseRefName): a code review that had passed waits again when the pull request gets a new commit or a new target branch. Engineering Memory then moves the task's work item as the project's workflow rules say, for example to PR when every pull request of the work item is ready and to READY FOR QA when they are all merged. It works from any chat, also for a delivery session.entry lists. What is recorded is what was reported: Engineering Memory does not check it with the provider, and performs no Git or provider action.",
516
651
  inputSchema: z.strictObject({
517
652
  projectId: z.string().uuid(),
518
653
  taskId: z.string().uuid(),
519
654
  url: z.string().trim().min(1).max(500),
520
655
  state: z.enum(['draft', 'ready', 'merged', 'closed']),
656
+ headCommit: z
657
+ .string()
658
+ .regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/)
659
+ .optional(),
660
+ targetBranch: z.string().trim().min(1).max(255).optional(),
521
661
  }),
522
662
  }, async (input) => {
523
663
  const result = await service.taskReviewRequest(input);
524
664
  if (!result.ok)
525
665
  return output(result);
526
666
  const recorded = result.data;
667
+ const reopened = recorded.data?.reviewReopened;
668
+ const job = recorded.data?.providerJob;
527
669
  return output({
528
670
  ok: true,
529
671
  data: {
530
672
  ...result.data,
531
673
  nextAction: recorded.queued
532
674
  ? 'Engineering Memory is unreachable, so this report is queued. It is recorded, and the work item moves as the rules say, when Engineering Memory is reachable again.'
533
- : (recorded.data?.reviewEvent?.nextAction ??
534
- (recorded.data?.reviewEvent
535
- ? 'The pull request is recorded; the work item stays where it is.'
536
- : 'The pull request is recorded. The work item does not move yet: other work on it is still open or undelivered, or the task has no active work item.')),
675
+ : [
676
+ reopened
677
+ ? `The code review of this delivery had passed, but the pull request has ${reopened === 'new_target' ? 'a new target branch' : 'a new commit'} now, so the review waits again. Tell the user in one line; review.start runs the next round.`
678
+ : '',
679
+ job === 'make_draft'
680
+ ? 'This project requires code review, so turn the pull request back into a draft with your own tools (on GitHub, gh pr ready --undo) and record it with state draft.'
681
+ : job === 'make_ready'
682
+ ? 'Its code review has already ended: review.make_ready makes the pull request ready.'
683
+ : '',
684
+ recorded.data?.reviewEvent?.nextAction ??
685
+ (recorded.data?.reviewEvent
686
+ ? 'The pull request is recorded; the work item stays where it is.'
687
+ : 'The pull request is recorded. The work item does not move yet: other work on it is still open or undelivered, or the task has no active work item.'),
688
+ ]
689
+ .filter(Boolean)
690
+ .join(' '),
537
691
  },
538
692
  });
539
693
  });