@@ -252,12 +252,28 @@ jobs:
252252 return match ? asDate(match[1]) : null;
253253 }
254254
255+ function requestCommentIdForRun(run) {
256+ const summary = String(run.output?.summary || '');
257+ const match = summary.match(/^Request comment: (\d+)$/m);
258+ return match ? Number(match[1]) : null;
259+ }
260+
261+ function requestBoundApprovalForRun(run) {
262+ const summary = String(run.output?.summary || '');
263+ return (
264+ run.output?.title === 'Codex review required after base update' ||
265+ /^Approval mode: request-reaction$/m.test(summary)
266+ );
267+ }
268+
255269 async function inspectCodexOutcome(
256270 pull,
257271 run,
258272 quotaFloor,
259- allowTerminal ,
273+ allowApproval ,
260274 terminalFloor,
275+ requestCommentId,
276+ requestBoundApproval,
261277 ) {
262278 const startedAt = asDate(run.started_at || run.created_at) || now;
263279 const resultFloor =
@@ -269,7 +285,7 @@ jobs:
269285 ? quotaFloor
270286 : resultFloor;
271287
272- const [reviews, reviewComments, issueComments] =
288+ const [reviews, reviewComments, issueComments, requestReactions ] =
273289 await Promise.all([
274290 github.paginate(github.rest.pulls.listReviews, {
275291 owner,
@@ -289,6 +305,14 @@ jobs:
289305 issue_number: pull.number,
290306 per_page: 100,
291307 }),
308+ requestCommentId
309+ ? github.paginate(github.rest.reactions.listForIssueComment, {
310+ owner,
311+ repo,
312+ comment_id: requestCommentId,
313+ per_page: 100,
314+ })
315+ : Promise.resolve([]),
292316 ]);
293317
294318 const commentsByReview = new Map();
@@ -303,7 +327,6 @@ jobs:
303327
304328 const terminal = [];
305329 for (const review of reviews) {
306- if (!allowTerminal) continue;
307330 if (!isCodex(review.user?.login)) continue;
308331 if (review.commit_id !== pull.head.sha) continue;
309332 const submittedAt = asDate(review.submitted_at);
@@ -329,7 +352,7 @@ jobs:
329352 }
330353
331354 for (const comment of issueComments) {
332- if (!allowTerminal ) continue;
355+ if (!allowApproval || requestBoundApproval ) continue;
333356 if (!isCodex(comment.user?.login)) continue;
334357 const body = String(comment.body || '');
335358 if (!body.startsWith("Codex Review: Didn't find any major issues.")) {
@@ -378,6 +401,16 @@ jobs:
378401 terminal.push({ at: createdAt, kind: 'approve', findingCount: 0 });
379402 }
380403
404+ if (allowApproval && requestBoundApproval && requestCommentId) {
405+ for (const reaction of requestReactions) {
406+ if (!isCodex(reaction.user?.login)) continue;
407+ if (reaction.content !== '+1') continue;
408+ const createdAt = asDate(reaction.created_at);
409+ if (!createdAt || createdAt < resultFloor) continue;
410+ terminal.push({ at: createdAt, kind: 'approve', findingCount: 0 });
411+ }
412+ }
413+
381414 terminal.sort((a, b) => {
382415 if (a.kind !== b.kind) return a.kind === 'findings' ? -1 : 1;
383416 return b.at - a.at;
@@ -438,13 +471,17 @@ jobs:
438471 const requestFloor = requestedAtForRun(run);
439472 const requiresFreshRequest =
440473 run.output?.title === 'Codex review required after base update';
474+ const requestCommentId = requestCommentIdForRun(run);
475+ const requestBoundApproval = requestBoundApprovalForRun(run);
441476
442477 const outcome = await inspectCodexOutcome(
443478 pull,
444479 run,
445480 availableSince,
446481 !requiresFreshRequest || Boolean(requestFloor),
447482 requestFloor,
483+ requestCommentId,
484+ requestBoundApproval,
448485 );
449486 if (outcome.terminal) {
450487 if (completedSuccess && outcome.terminal.kind !== 'findings') {
@@ -486,7 +523,10 @@ jobs:
486523 repo,
487524 pull_number: event.pr,
488525 });
489- if (current.data.head.sha !== event.head_sha) {
526+ if (
527+ current.data.head.sha !== event.head_sha ||
528+ current.data.base.sha !== event.base_sha
529+ ) {
490530 await github.rest.checks.update({
491531 owner,
492532 repo,
@@ -496,7 +536,7 @@ jobs:
496536 completed_at: now.toISOString(),
497537 output: {
498538 title: 'Codex review superseded',
499- summary: 'The pull request changed while its Codex result was being reconciled.',
539+ summary: 'The pull request head or base changed while its Codex result was being reconciled.',
500540 },
501541 });
502542 continue;
@@ -662,7 +702,9 @@ jobs:
662702 title: 'Codex review requested',
663703 summary: [
664704 `Requested at: ${now.toISOString()}`,
705+ `Request comment: ${Number(comment.data.id)}`,
665706 `Base commit: ${candidate.base_sha}`,
707+ 'Approval mode: request-reaction',
666708 'Requested managed Codex review and waiting for its result.',
667709 ].join('\n'),
668710 },
0 commit comments