ReviewOS

also looking at this

stacks/buddy

fix(ci): give the repair loop the guard it already had

#1407
Merged glennmichael123 wants to merge fix/wire-ci-repair-attempt-guard into main
10 files +229 -34
bin/cli.tsmodified+28-3
Changes to bin/cli.ts
@@ -3,7 +3,7 @@
33import type { GitProvider } from '../src/git/provider'
44import type { ReviewFinding } from '../src/review/findings'
55import type { ReviewFormat } from '../src/review/local'
6import type { BuddyConfig } from '../src/types'
6import type { BuddyConfig, PullRequest } from '../src/types'
77import fs from 'node:fs'
88import process from 'node:process'
99import { CLI } from '@stacksjs/clapp'
@@ -1354,10 +1354,22 @@ cli
13541354 }
13551355
13561356 const { attemptFix } = await import('../src/ci')
1357 const { parseFixAttempts, upsertFixAttempts } = await import('../src/ci/attempts')
13571358 const { createAiClient } = await import('../src/ai')
13581359
1360 // The guard in attemptFix can only stop a loop if it is told how many
1361 // attempts came before, and a CI job remembers nothing between runs.
1362 const prNumber = options.pr ? Number.parseInt(options.pr, 10) : undefined
1363 let pullRequest: PullRequest | undefined
1364 if (prNumber) {
1365 const open = await provider.getPullRequests('open')
1366 pullRequest = open.find(candidate => candidate.number === prNumber)
1367 }
1368 const priorAttempts = parseFixAttempts(pullRequest?.body)?.attempts ?? 0
1369
13591370 const outcome = await attemptFix({
13601371 log,
1372 priorAttempts,
13611373 workspace: process.cwd(),
13621374 baseBranch: config.repository?.baseBranch ?? 'main',
13631375 ai: createAiClient(config, logger),
@@ -1384,10 +1396,23 @@ cli
13841396
13851397 logger.info(`\n${outcome.report}\n`)
13861398
1387 const prNumber = options.pr ? Number.parseInt(options.pr, 10) : undefined
1388 if (prNumber && !options.dryRun)
1399 if (prNumber && !options.dryRun) {
13891400 await provider.createComment(prNumber, outcome.report)
13901401
1402 // Only a real attempt counts. Recording the ones the guard already
1403 // refused would march the counter up without anything being tried.
1404 if (outcome.action !== 'skipped' && pullRequest) {
1405 try {
1406 await provider.updatePullRequest(prNumber, {
1407 body: upsertFixAttempts(pullRequest.body, priorAttempts + 1),
1408 })
1409 }
1410 catch (error) {
1411 logger.warn(`Could not record the fix attempt on PR #${prNumber}: ${error instanceof Error ? error.message : String(error)}`)
1412 }
1413 }
1414 }
1415
13911416 logger.success(`✅ ${outcome.action}${outcome.fixed ? ' (fixed)' : ''}`)
13921417 }
13931418 catch (error) {