@alecsibilia/luca 14.0.0-alpha.2 → 14.0.0-alpha.21
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.
- package/CHANGELOG.md +76 -0
- package/README.md +7 -2
- package/board/client/board-look.ts +31 -1
- package/board/client/board-panel-parts.tsx +150 -0
- package/board/client/board-panel-stages.ts +99 -0
- package/board/client/board-panel-styles.ts +223 -0
- package/board/client/board-panel.tsx +77 -1275
- package/board/client/engine-life.ts +48 -0
- package/board/client/final-review-stack.tsx +144 -0
- package/board/client/heartbeat.tsx +61 -0
- package/board/client/helper-lines.tsx +69 -0
- package/board/client/live-feed.tsx +86 -0
- package/board/client/live-line.ts +73 -0
- package/board/client/model-line.tsx +178 -0
- package/board/client/needs-you-section.tsx +133 -0
- package/board/client/one-line.tsx +193 -0
- package/board/client/repo-wait-banner.tsx +69 -0
- package/board/client/run-footer.tsx +68 -0
- package/board/client/run-header.tsx +189 -0
- package/board/client/run-picker.tsx +98 -0
- package/board/client/status-bar.tsx +131 -0
- package/board/client/step-dots.tsx +127 -0
- package/board/client/stuck-actions.tsx +269 -0
- package/board/client/ticket-card-view.tsx +238 -0
- package/board/client/ticket-detail.tsx +87 -0
- package/board/client/ticket-stack.tsx +119 -0
- package/board/client/ticket-wait.ts +188 -0
- package/board/client/timeline-rows.tsx +41 -14
- package/board/client/usage-line.tsx +51 -0
- package/board/client/wait-line.tsx +39 -0
- package/board/index.client.tsx +1 -1
- package/board/index.server.ts +7 -1
- package/board/package.json +1 -1
- package/board/server/board-panel-state-helpers.ts +67 -0
- package/board/server/board-server-check.ts +294 -0
- package/board/server/board-server-engine.ts +129 -0
- package/board/server/board-server-events.ts +134 -0
- package/board/server/board-server-read.ts +190 -0
- package/board/server/board-server-resume.ts +164 -0
- package/board/server/board-server-start.ts +174 -0
- package/board/server/board-server-store.ts +281 -0
- package/board/server/board-server-types.ts +114 -0
- package/board/server/board-server-util.ts +4 -0
- package/board/server/board-server.ts +52 -943
- package/board/server/board-vocabulary-engine.ts +144 -0
- package/board/server/board-vocabulary-review.ts +115 -0
- package/board/server/board-vocabulary-run.ts +145 -0
- package/board/server/board-vocabulary-shapes.ts +153 -0
- package/board/server/board-vocabulary.ts +20 -375
- package/board/server/current-step-panel-helpers.ts +108 -0
- package/board/server/describe-record.ts +403 -0
- package/board/server/engine-launch.ts +20 -3
- package/board/server/engine-restart-helpers.ts +105 -0
- package/board/server/engine-watch.ts +45 -5
- package/board/server/journal-rebuild-helpers.ts +169 -0
- package/board/server/live-file.ts +130 -0
- package/board/server/luca-version.ts +1 -1
- package/board/server/make-rows.ts +14 -629
- package/board/server/record-texts.ts +350 -0
- package/board/server/reduce-board-agent-start.ts +224 -0
- package/board/server/reduce-board-agents.ts +160 -0
- package/board/server/reduce-board-counters.ts +138 -0
- package/board/server/reduce-board-create.ts +148 -0
- package/board/server/reduce-board-final-review.ts +174 -0
- package/board/server/reduce-board-needs-you.ts +283 -0
- package/board/server/reduce-board-reasons.ts +59 -0
- package/board/server/reduce-board-run.ts +255 -0
- package/board/server/reduce-board-settle.ts +88 -0
- package/board/server/reduce-board-steps.ts +226 -0
- package/board/server/reduce-board-text.ts +139 -0
- package/board/server/reduce-board-ticket-checks.ts +168 -0
- package/board/server/reduce-board-ticket-join.ts +144 -0
- package/board/server/reduce-board-ticket-start.ts +132 -0
- package/board/server/reduce-board-update.ts +117 -0
- package/board/server/reduce-board-usage.ts +247 -0
- package/board/server/reduce-board.ts +100 -1917
- package/board/server/reply-post.ts +407 -0
- package/board/server/run-registry.ts +8 -4
- package/board/server/ticket-stages-panel-helpers.ts +296 -0
- package/board/shared/board-rows.ts +9 -0
- package/board/shared/board-rpc.ts +42 -0
- package/board/shared/board-state-counts.ts +77 -0
- package/board/shared/board-state-live.ts +99 -0
- package/board/shared/board-state-review.ts +59 -0
- package/board/shared/board-state-run.ts +236 -0
- package/board/shared/board-state-ticket.ts +257 -0
- package/board/shared/board-state-usage.ts +107 -0
- package/board/shared/board-state.ts +97 -567
- package/board/shared/reply-actions.ts +207 -0
- package/engine/agents/agent-launcher.ts +27 -4
- package/engine/agents/claude-launcher-helpers.ts +233 -0
- package/engine/agents/claude-launcher.ts +28 -669
- package/engine/agents/claude-messages.ts +212 -0
- package/engine/agents/claude-options.ts +43 -8
- package/engine/agents/common-instructions.ts +138 -0
- package/engine/agents/input-channel.ts +36 -0
- package/engine/agents/launch-paths.ts +29 -0
- package/engine/agents/launch-session.ts +227 -0
- package/engine/agents/learner-instructions.ts +31 -0
- package/engine/agents/open-session.ts +210 -0
- package/engine/agents/prompt-sections.ts +95 -0
- package/engine/agents/rejoin-prompts.ts +135 -0
- package/engine/agents/reviewer-instructions.ts +99 -0
- package/engine/agents/role-instructions.ts +31 -244
- package/engine/agents/role-prompts.ts +39 -109
- package/engine/agents/role-results.ts +106 -7
- package/engine/agents/scripted-launcher.ts +21 -6
- package/engine/agents/session-pool.ts +124 -0
- package/engine/agents/turn-progress.ts +283 -0
- package/engine/agents/writer-instructions.ts +151 -0
- package/engine/cli/computer-adapters-real.ts +15 -3
- package/engine/cli/computer-adapters.ts +2 -0
- package/engine/cli/computer-checks.ts +87 -4
- package/engine/cli/config-reload-text.ts +39 -0
- package/engine/cli/decision-model-setup.ts +70 -0
- package/engine/cli/doctor-command.ts +1 -0
- package/engine/cli/doctor.ts +21 -3
- package/engine/cli/going-runs.ts +192 -36
- package/engine/cli/init-command.ts +1 -0
- package/engine/cli/init.ts +23 -1
- package/engine/cli/live-runs.ts +73 -0
- package/engine/cli/luca-run.ts +42 -7
- package/engine/cli/luca-skills.ts +183 -0
- package/engine/cli/luca.ts +8 -1
- package/engine/cli/models-command.ts +119 -0
- package/engine/cli/read-run.ts +81 -0
- package/engine/cli/run-args.ts +33 -6
- package/engine/cli/run-demo.ts +143 -0
- package/engine/cli/run-end.ts +184 -0
- package/engine/cli/run-modes-helpers.ts +81 -0
- package/engine/cli/run-modes.ts +62 -439
- package/engine/cli/setup.ts +5 -48
- package/engine/cli/unfinished-runs.ts +122 -0
- package/engine/cli/upgrade-command.ts +212 -30
- package/engine/cli/upgrade-finish.ts +149 -0
- package/engine/cli/upgrade.ts +112 -71
- package/engine/config/config-fields.ts +127 -0
- package/engine/config/config-reload.ts +250 -0
- package/engine/config/engine-config-schema.ts +235 -0
- package/engine/config/engine-config.ts +72 -142
- package/engine/config/test-commands.ts +102 -0
- package/engine/core/agent-guards-helpers.ts +84 -0
- package/engine/core/agent-turn.ts +395 -0
- package/engine/core/already-done.ts +44 -1
- package/engine/core/build-action.ts +214 -0
- package/engine/core/build-context.ts +135 -0
- package/engine/core/build-launch.ts +259 -0
- package/engine/core/commit-in.ts +282 -0
- package/engine/core/decide-build-early.ts +108 -0
- package/engine/core/decide-build-ending.ts +145 -0
- package/engine/core/decide-build-helpers.ts +87 -0
- package/engine/core/decide-build-tickets.ts +207 -0
- package/engine/core/decide-build-view.ts +103 -0
- package/engine/core/decide-build.ts +101 -1368
- package/engine/core/decide-final-fix.ts +203 -0
- package/engine/core/decide-final-review.ts +55 -310
- package/engine/core/decide-red-green.ts +398 -0
- package/engine/core/decide-rejoin.ts +259 -0
- package/engine/core/decide-repo-wait.ts +129 -0
- package/engine/core/decide-run-budget.ts +86 -12
- package/engine/core/decide-stuck-helpers.ts +127 -0
- package/engine/core/decide-stuck.ts +60 -8
- package/engine/core/decide-ticket-review.ts +95 -0
- package/engine/core/decide-ticket.ts +267 -0
- package/engine/core/decide-waits.ts +47 -0
- package/engine/core/decide.ts +74 -23
- package/engine/core/engine-chores.ts +163 -0
- package/engine/core/environment-rerun.ts +83 -0
- package/engine/core/evidence-text.ts +84 -0
- package/engine/core/execute-action.ts +303 -0
- package/engine/core/execute-already-done-check.ts +133 -0
- package/engine/core/execute-build.ts +68 -944
- package/engine/core/execute-changeset.ts +5 -7
- package/engine/core/execute-final-review.ts +125 -13
- package/engine/core/execute-gates.ts +182 -0
- package/engine/core/execute-intake.ts +130 -0
- package/engine/core/execute-join.ts +276 -0
- package/engine/core/execute-memory.ts +12 -0
- package/engine/core/execute-pull-request.ts +84 -0
- package/engine/core/execute-red-check.ts +219 -0
- package/engine/core/execute-repo-wait.ts +129 -0
- package/engine/core/execute-stuck.ts +25 -10
- package/engine/core/execute-waits.ts +158 -0
- package/engine/core/execute-with-jev.ts +118 -0
- package/engine/core/execute.ts +124 -831
- package/engine/core/final-failure-cap.ts +38 -0
- package/engine/core/final-review-action.ts +125 -0
- package/engine/core/final-review-text.ts +63 -17
- package/engine/core/final-stuck-text.ts +86 -0
- package/engine/core/fix-loop-text.ts +49 -2
- package/engine/core/left-out.ts +120 -0
- package/engine/core/live-file.ts +372 -0
- package/engine/core/loop-caps.ts +25 -2
- package/engine/core/not-in-run.ts +7 -3
- package/engine/core/pull-request-text.ts +255 -6
- package/engine/core/repo-wait-text.ts +81 -0
- package/engine/core/review-text.ts +55 -1
- package/engine/core/run-crash-recovery-helpers.ts +16 -0
- package/engine/core/run-session-close-helpers.ts +96 -0
- package/engine/core/run-start.ts +148 -0
- package/engine/core/run-stuck-text.ts +86 -0
- package/engine/core/schedule-rules.ts +193 -0
- package/engine/core/stuck-detail-text.ts +89 -0
- package/engine/core/stuck-format.ts +22 -0
- package/engine/core/stuck-text.ts +29 -332
- package/engine/core/ticket-stuck-text.ts +192 -0
- package/engine/core/worktree-changes.ts +106 -0
- package/engine/gates/already-done-check.ts +120 -0
- package/engine/gates/feature-map.ts +96 -0
- package/engine/gates/gate-runner.ts +108 -108
- package/engine/gates/gate-schemas.ts +61 -2
- package/engine/gates/leftover-scan.ts +154 -31
- package/engine/gates/new-tests.ts +45 -0
- package/engine/gates/own-files-check.ts +98 -0
- package/engine/gates/prepare-check.ts +60 -0
- package/engine/gates/prepare-slots.ts +99 -0
- package/engine/gates/red-check.ts +184 -16
- package/engine/gates/shell-check.ts +64 -0
- package/engine/gates/slot-waits.ts +55 -0
- package/engine/gates/slow-test-runner.ts +59 -0
- package/engine/gates/test-checks.ts +161 -0
- package/engine/gates/test-runner.ts +164 -5
- package/engine/gates/test-slots.ts +48 -0
- package/engine/git/git-adapter.ts +55 -396
- package/engine/git/git-commits.ts +132 -0
- package/engine/git/git-join.ts +139 -0
- package/engine/git/git-read.ts +123 -0
- package/engine/git/git-run.ts +32 -0
- package/engine/git/git-worktrees.ts +143 -0
- package/engine/git/outside-links.ts +55 -0
- package/engine/guards/after-turn-check.ts +31 -5
- package/engine/guards/guard-hook.ts +60 -12
- package/engine/guards/guard-roles.ts +68 -0
- package/engine/guards/helper-rules.ts +92 -0
- package/engine/guards/permission-rules.ts +161 -0
- package/engine/guards/role-rules-helpers.ts +48 -0
- package/engine/guards/role-rules.ts +34 -606
- package/engine/guards/shell-rules.ts +344 -0
- package/engine/guards/worktree-git.ts +208 -0
- package/engine/guards/worktree-snapshot.ts +412 -0
- package/engine/guards/worktree-state.ts +28 -825
- package/engine/guards/worktree-undo.ts +260 -0
- package/engine/guards/write-rules.ts +149 -0
- package/engine/index.ts +30 -9
- package/engine/intake/criteria-checks.ts +114 -0
- package/engine/intake/intake-checks.ts +164 -30
- package/engine/intake/intake-schemas.ts +37 -2
- package/engine/jev/clef-client.ts +259 -0
- package/engine/jev/decision-model-credentials.ts +212 -0
- package/engine/jev/jev-jobs.ts +144 -60
- package/engine/jev/jev-schemas.ts +86 -5
- package/engine/jev/jev-shadow-helpers.ts +218 -0
- package/engine/jev/jev-shadow.ts +109 -64
- package/engine/jev/model-pick.ts +275 -0
- package/engine/journal/agent-entries.ts +167 -0
- package/engine/journal/agent-message-entries.ts +72 -0
- package/engine/journal/agent-turn-replay.ts +203 -0
- package/engine/journal/already-done-entries.ts +55 -0
- package/engine/journal/commit-entries.ts +56 -0
- package/engine/journal/cross-ticket-replay.ts +222 -0
- package/engine/journal/decision-model-entries.ts +86 -0
- package/engine/journal/entry-fields.ts +10 -0
- package/engine/journal/final-review-entries.ts +128 -0
- package/engine/journal/final-review-replay.ts +324 -0
- package/engine/journal/final-review-round.ts +320 -0
- package/engine/journal/final-review-state.ts +180 -0
- package/engine/journal/gate-entries.ts +81 -0
- package/engine/journal/intake-entries.ts +87 -0
- package/engine/journal/join-entries.ts +126 -0
- package/engine/journal/journal-entry-schema.ts +218 -0
- package/engine/journal/journal-kinds.ts +97 -0
- package/engine/journal/journal-record-schema.ts +218 -0
- package/engine/journal/journal-record.ts +43 -1400
- package/engine/journal/journal.ts +6 -1
- package/engine/journal/limit-entries.ts +90 -0
- package/engine/journal/memory-entries.ts +124 -0
- package/engine/journal/memory-replay.ts +157 -0
- package/engine/journal/plan-replay.ts +139 -0
- package/engine/journal/pull-request-entries.ts +41 -0
- package/engine/journal/replay.ts +157 -2144
- package/engine/journal/reply-replay.ts +144 -0
- package/engine/journal/repo-wait-entries.ts +53 -0
- package/engine/journal/run-entries.ts +172 -0
- package/engine/journal/run-state.ts +264 -0
- package/engine/journal/shared-failure-replay.ts +133 -0
- package/engine/journal/stuck-entries.ts +193 -0
- package/engine/journal/ticket-progress.ts +392 -0
- package/engine/journal/ticket-replay.ts +411 -0
- package/engine/journal/ticket-result-replay.ts +364 -0
- package/engine/journal/ticket-retry-replay.ts +142 -0
- package/engine/journal/worktree-entries.ts +122 -0
- package/engine/models/model-score.ts +512 -0
- package/engine/shell/run-command.ts +187 -18
- package/engine/testing/agent-turn-fixtures.ts +287 -0
- package/engine/testing/already-done-fixtures.ts +97 -0
- package/engine/testing/build-fixtures.ts +68 -973
- package/engine/testing/checkouts-fixtures.ts +48 -0
- package/engine/testing/final-review-fixtures.ts +62 -1
- package/engine/testing/final-review-steps.ts +143 -0
- package/engine/testing/limit-fixtures.ts +85 -0
- package/engine/testing/live-file-fixtures.ts +57 -0
- package/engine/testing/practice-repo.ts +10 -4
- package/engine/testing/practice-run.ts +1 -1
- package/engine/testing/reply-fixtures.ts +88 -0
- package/engine/testing/run-budget-fixtures.ts +165 -0
- package/engine/testing/run-fixtures.ts +219 -0
- package/engine/testing/sdk-play.ts +177 -0
- package/engine/testing/setup-repo.ts +194 -0
- package/engine/testing/step-record-fixtures.ts +55 -0
- package/engine/testing/ticket-step-fixtures.ts +272 -0
- package/engine/testing/wait-scenarios.ts +186 -0
- package/engine/tracker/gh-retry.ts +217 -0
- package/engine/tracker/github-tracker.ts +310 -90
- package/engine/tracker/in-memory-tracker.ts +8 -4
- package/engine/tracker/tracker.ts +131 -6
- package/package.json +2 -1
- package/skills/luca-retro/SKILL.md +74 -0
- package/skills/luca-retro/scripts/retro-summary.ts +1356 -0
- package/skills/luca-unstick/SKILL.md +153 -0
- package/skills/luca-unstick/scripts/stuck-summary.ts +1063 -0
- package/engine/jev/jev-client.ts +0 -173
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The final review's fix round (`fixStep`): the fixers, the gates and their
|
|
3
|
+
* fix loop, the fix commit, and the push, after the lenses found blocking
|
|
4
|
+
* findings. Pure.
|
|
5
|
+
*/
|
|
6
|
+
import { environmentFailedDetail } from './environment-rerun'
|
|
7
|
+
import { failureCap } from './final-failure-cap'
|
|
8
|
+
import {
|
|
9
|
+
stuck,
|
|
10
|
+
type DecideArgs,
|
|
11
|
+
type FinalReviewAction,
|
|
12
|
+
} from './final-review-action'
|
|
13
|
+
import {
|
|
14
|
+
finalFixerPrompt,
|
|
15
|
+
openFinalFindingsText,
|
|
16
|
+
type FinalFixerRole,
|
|
17
|
+
} from './final-review-text'
|
|
18
|
+
import {
|
|
19
|
+
CRASH_SECTION,
|
|
20
|
+
failedChecks,
|
|
21
|
+
failedTryMessage,
|
|
22
|
+
followUpSection,
|
|
23
|
+
gateFixMessage,
|
|
24
|
+
} from './fix-loop-text'
|
|
25
|
+
import { MAX_FIX_ROUNDS } from './loop-caps'
|
|
26
|
+
import { badTestDetail, setupChangeDetail } from './stuck-text'
|
|
27
|
+
|
|
28
|
+
import type { AgentRole } from '../agents/role-results'
|
|
29
|
+
import { handoffRole, type ReplayedAgentFailure } from '../journal/replay'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A fresh fixer of this role, told its findings (or the failed gates). With
|
|
33
|
+
* `follow_up`, it stands in for a follow-up whose session is gone, and its
|
|
34
|
+
* prompt carries the message. A fixer taking a turn a crash cut off is told
|
|
35
|
+
* of the crash.
|
|
36
|
+
*/
|
|
37
|
+
const launchFixer = ({
|
|
38
|
+
role,
|
|
39
|
+
review,
|
|
40
|
+
snapshot,
|
|
41
|
+
run_notes,
|
|
42
|
+
follow_up,
|
|
43
|
+
}: DecideArgs & {
|
|
44
|
+
role: FinalFixerRole
|
|
45
|
+
follow_up?: string
|
|
46
|
+
}): FinalReviewAction => {
|
|
47
|
+
const { fix } = review
|
|
48
|
+
if (fix === null) throw new Error('No final review fix round is open.')
|
|
49
|
+
// An implementer with no code findings left is here for failed gates.
|
|
50
|
+
const gates =
|
|
51
|
+
role === 'implementer' && fix.code_answered ? review.gates : null
|
|
52
|
+
const sections = [
|
|
53
|
+
...(follow_up === undefined
|
|
54
|
+
? []
|
|
55
|
+
: [followUpSection({ message: follow_up })]),
|
|
56
|
+
...(review.crashed_turn === role ? [CRASH_SECTION] : []),
|
|
57
|
+
]
|
|
58
|
+
return {
|
|
59
|
+
type: 'launch_final_fixer',
|
|
60
|
+
role,
|
|
61
|
+
prompt: finalFixerPrompt({
|
|
62
|
+
role,
|
|
63
|
+
snapshot,
|
|
64
|
+
fix,
|
|
65
|
+
gates,
|
|
66
|
+
run_notes,
|
|
67
|
+
sections,
|
|
68
|
+
}),
|
|
69
|
+
may_edit_tests: role === 'test-writer',
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const isFixer = (role: AgentRole): role is FinalFixerRole =>
|
|
74
|
+
role === 'test-writer' || role === 'implementer'
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The next step after a fixer's failed turn: an engine failure starts a
|
|
78
|
+
* fresh fixer, any other a follow-up in its session (a fresh fixer with the
|
|
79
|
+
* same message with none), until its tries run out.
|
|
80
|
+
*/
|
|
81
|
+
const failedFixerStep = ({
|
|
82
|
+
failed,
|
|
83
|
+
...args
|
|
84
|
+
}: DecideArgs & { failed: ReplayedAgentFailure }): FinalReviewAction => {
|
|
85
|
+
const cap = failureCap({ review: args.review, failed })
|
|
86
|
+
if (cap !== null) return cap
|
|
87
|
+
const { role, error, failure, session_id } = failed
|
|
88
|
+
if (!isFixer(role)) throw new Error(`${role} is no final review fixer.`)
|
|
89
|
+
if (failure === 'engine') return launchFixer({ ...args, role })
|
|
90
|
+
const message = failedTryMessage({ failure, error })
|
|
91
|
+
if (session_id === null) {
|
|
92
|
+
return launchFixer({ ...args, role, follow_up: message })
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
type: 'follow_up_final_fixer',
|
|
96
|
+
role,
|
|
97
|
+
session_id,
|
|
98
|
+
message,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A final review fix round, once every due lens finished and some finding
|
|
104
|
+
* blocks: start the round, a fresh test-writer for the test findings, then
|
|
105
|
+
* a fresh implementer for the code findings, then a fresh agent of the
|
|
106
|
+
* other writer role for findings a fixer answered `other_role` (#616), the
|
|
107
|
+
* gates (and their fix loop in the implementer's session, or a rerun when
|
|
108
|
+
* it answers `environment`, #613), the fix commit, and the push. `null` once
|
|
109
|
+
* the fixes are pushed.
|
|
110
|
+
*/
|
|
111
|
+
export const fixStep = (args: DecideArgs): FinalReviewAction | null => {
|
|
112
|
+
const { review, run_branch, snapshot } = args
|
|
113
|
+
const { fix } = review
|
|
114
|
+
if (fix === null) return null
|
|
115
|
+
// The fresh pass's one fix round runs past the cap (#548).
|
|
116
|
+
if (fix.round > MAX_FIX_ROUNDS && !review.fresh) {
|
|
117
|
+
return stuck({
|
|
118
|
+
reason: 'changes_requested',
|
|
119
|
+
detail: `The final review still asks for changes after ${MAX_FIX_ROUNDS} fix rounds:\n${openFinalFindingsText({ findings: fix.findings })}`,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
if (!review.fixing_started) {
|
|
123
|
+
return { type: 'start_final_fix', round: fix.round }
|
|
124
|
+
}
|
|
125
|
+
const failed = [
|
|
126
|
+
review.agent_failures['test-writer'],
|
|
127
|
+
review.agent_failures.implementer,
|
|
128
|
+
].find((entry) => entry !== undefined)
|
|
129
|
+
if (failed !== undefined) return failedFixerStep({ ...args, failed })
|
|
130
|
+
if (review.setup_change !== null) {
|
|
131
|
+
return stuck({
|
|
132
|
+
reason: 'setup_change_needed',
|
|
133
|
+
detail: setupChangeDetail({
|
|
134
|
+
...review.setup_change,
|
|
135
|
+
setup_files: args.setup_files ?? [],
|
|
136
|
+
}),
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
if (!fix.tests_answered)
|
|
140
|
+
return launchFixer({ ...args, role: 'test-writer' })
|
|
141
|
+
if (!fix.code_answered) return launchFixer({ ...args, role: 'implementer' })
|
|
142
|
+
if (fix.bad_test !== null) {
|
|
143
|
+
return stuck({
|
|
144
|
+
reason: 'bad_test',
|
|
145
|
+
detail: `While fixing the final review's findings, the implementer sent a test back as bad: ${badTestDetail({ bad_test: fix.bad_test })}`,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
const handoff = handoffRole({ fix })
|
|
149
|
+
if (handoff !== null) return launchFixer({ ...args, role: handoff })
|
|
150
|
+
const { gates } = review
|
|
151
|
+
const blamed = environmentFailedDetail({
|
|
152
|
+
environment: review.environment,
|
|
153
|
+
reruns: review.environment_reruns,
|
|
154
|
+
latest: gates,
|
|
155
|
+
})
|
|
156
|
+
if (blamed !== null) {
|
|
157
|
+
return stuck({ reason: 'environment_failed', detail: blamed })
|
|
158
|
+
}
|
|
159
|
+
if (gates === null) return { type: 'run_final_gates' }
|
|
160
|
+
if (!gates.ok) {
|
|
161
|
+
if (review.gate_fix_rounds >= MAX_FIX_ROUNDS) {
|
|
162
|
+
return stuck({
|
|
163
|
+
reason: 'gates_failed',
|
|
164
|
+
detail: `The gates still fail after the final review's fixes, after ${MAX_FIX_ROUNDS} fix rounds:\n${failedChecks({ gates })}`,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
const session_id = review.sessions.implementer
|
|
168
|
+
const message = gateFixMessage({ gates })
|
|
169
|
+
if (session_id === undefined) {
|
|
170
|
+
return launchFixer({
|
|
171
|
+
...args,
|
|
172
|
+
role: 'implementer',
|
|
173
|
+
follow_up: message,
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
type: 'follow_up_final_fixer',
|
|
178
|
+
role: 'implementer',
|
|
179
|
+
session_id,
|
|
180
|
+
message,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (review.commit === null) {
|
|
184
|
+
const hits = review.leftovers
|
|
185
|
+
if (hits !== null && hits.length > 0) {
|
|
186
|
+
return stuck({
|
|
187
|
+
reason: 'leftovers_found',
|
|
188
|
+
detail: hits
|
|
189
|
+
.map(({ path, reason }) => `${path}: ${reason}`)
|
|
190
|
+
.join('\n'),
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
type: 'commit_final_fix',
|
|
195
|
+
round: fix.round,
|
|
196
|
+
message: `fix: final review round ${fix.round} for spec #${snapshot.spec.number}`,
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (review.pushed === null) {
|
|
200
|
+
return { type: 'push_final_fixes', branch: run_branch.branch }
|
|
201
|
+
}
|
|
202
|
+
return null
|
|
203
|
+
}
|
|
@@ -1,136 +1,22 @@
|
|
|
1
1
|
import { crashDetail, crashedOut } from './decide-crashes'
|
|
2
|
+
import { fixStep } from './decide-final-fix'
|
|
3
|
+
import { failureCap } from './final-failure-cap'
|
|
2
4
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type
|
|
7
|
-
} from './final-review-
|
|
8
|
-
import {
|
|
9
|
-
CRASH_SECTION,
|
|
10
|
-
failedChecks,
|
|
11
|
-
failedTryMessage,
|
|
12
|
-
followUpSection,
|
|
13
|
-
gateFixMessage,
|
|
14
|
-
} from './fix-loop-text'
|
|
15
|
-
import { MAX_ENGINE_FAILURES, MAX_FIX_ROUNDS } from './loop-caps'
|
|
16
|
-
|
|
17
|
-
import type { PromptRunNote } from '../agents/role-prompts'
|
|
18
|
-
import {
|
|
19
|
-
LENS_NAMES,
|
|
20
|
-
lensRole,
|
|
21
|
-
type AgentRole,
|
|
22
|
-
type LensName,
|
|
23
|
-
type LensRole,
|
|
24
|
-
} from '../agents/role-results'
|
|
25
|
-
import type { StuckReason } from '../journal/journal-record'
|
|
26
|
-
import {
|
|
27
|
-
isFinalKey,
|
|
28
|
-
type FinalReviewState,
|
|
29
|
-
type ReplayedAgentFailure,
|
|
30
|
-
type ReplayedGates,
|
|
31
|
-
type ReplayedSnapshot,
|
|
32
|
-
type ReplayedWorktree,
|
|
33
|
-
} from '../journal/replay'
|
|
34
|
-
import type { CrashCounts } from '../journal/step-records'
|
|
35
|
-
|
|
36
|
-
/** The final review's steps, once every ticket pushed and before the PR. */
|
|
37
|
-
export type FinalReviewAction =
|
|
38
|
-
/**
|
|
39
|
-
* Start a final review round: read the run branch's HEAD, the files
|
|
40
|
-
* since `from_sha`, and the config's rule files, then journal
|
|
41
|
-
* `final_review_started`.
|
|
42
|
-
*/
|
|
43
|
-
| {
|
|
44
|
-
type: 'start_final_review'
|
|
45
|
-
round: number
|
|
46
|
-
from_sha: string
|
|
47
|
-
lenses: LensName[]
|
|
48
|
-
}
|
|
49
|
-
/** Start a fresh, read-only lens reviewer on the run branch. */
|
|
50
|
-
| { type: 'launch_lens'; lens: LensName; role: LensRole; prompt: string }
|
|
51
|
-
/** Every lens due this round approved. */
|
|
52
|
-
| { type: 'pass_final_review' }
|
|
53
|
-
/** A fix round on the lenses' findings starts. */
|
|
54
|
-
| { type: 'start_final_fix'; round: number }
|
|
55
|
-
/** Start a fresh fixer on the run branch's worktree. */
|
|
56
|
-
| {
|
|
57
|
-
type: 'launch_final_fixer'
|
|
58
|
-
role: FinalFixerRole
|
|
59
|
-
prompt: string
|
|
60
|
-
may_edit_tests: boolean
|
|
61
|
-
}
|
|
62
|
-
/** Send a follow-up to a fixer's open session (failed gates, a failed try). */
|
|
63
|
-
| {
|
|
64
|
-
type: 'follow_up_final_fixer'
|
|
65
|
-
role: FinalFixerRole
|
|
66
|
-
session_id: string
|
|
67
|
-
message: string
|
|
68
|
-
}
|
|
69
|
-
/** Run the engine config's gates on the run branch's worktree. */
|
|
70
|
-
| { type: 'run_final_gates' }
|
|
71
|
-
/** Scan for leftovers, then commit the fixes on the run branch. */
|
|
72
|
-
| { type: 'commit_final_fix'; round: number; message: string }
|
|
73
|
-
/** Push the run branch with the fixes to `origin`. */
|
|
74
|
-
| { type: 'push_final_fixes'; branch: string }
|
|
75
|
-
/** The engine can't safely pick the final review's next step by itself. */
|
|
76
|
-
| { type: 'mark_final_review_stuck'; reason: StuckReason; detail: string }
|
|
77
|
-
|
|
78
|
-
const FINAL_REVIEW_ACTION_TYPES: ReadonlySet<string> = new Set<
|
|
79
|
-
FinalReviewAction['type']
|
|
80
|
-
>([
|
|
81
|
-
'start_final_review',
|
|
82
|
-
'launch_lens',
|
|
83
|
-
'pass_final_review',
|
|
84
|
-
'start_final_fix',
|
|
85
|
-
'launch_final_fixer',
|
|
86
|
-
'follow_up_final_fixer',
|
|
87
|
-
'run_final_gates',
|
|
88
|
-
'commit_final_fix',
|
|
89
|
-
'push_final_fixes',
|
|
90
|
-
'mark_final_review_stuck',
|
|
91
|
-
])
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Whether an action is one of the final review's.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* isFinalReviewAction({ type: 'run_final_gates' }) // true
|
|
98
|
-
*/
|
|
99
|
-
export const isFinalReviewAction = <Action extends { type: string }>(
|
|
100
|
-
action: Action
|
|
101
|
-
): action is Extract<Action, FinalReviewAction> =>
|
|
102
|
-
FINAL_REVIEW_ACTION_TYPES.has(action.type)
|
|
5
|
+
stuck,
|
|
6
|
+
type DecideArgs,
|
|
7
|
+
type FinalReviewAction,
|
|
8
|
+
type FinalReviewDecision,
|
|
9
|
+
} from './final-review-action'
|
|
10
|
+
import { lensPrompt } from './final-review-text'
|
|
103
11
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* still working (these actions can run now), passed or shipped (open the
|
|
107
|
-
* PR), or stuck (end the run until a reply).
|
|
108
|
-
*/
|
|
109
|
-
export type FinalReviewDecision =
|
|
110
|
-
| { status: 'working'; actions: FinalReviewAction[] }
|
|
111
|
-
| { status: 'passed' }
|
|
112
|
-
| { status: 'shipped' }
|
|
113
|
-
| { status: 'stuck'; reason: StuckReason; detail: string }
|
|
12
|
+
import { LENS_NAMES, lensRole, type LensName } from '../agents/role-results'
|
|
13
|
+
import { isFinalKey } from '../journal/replay'
|
|
114
14
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
run_branch: ReplayedWorktree
|
|
121
|
-
/** The latest gates on the run branch. */
|
|
122
|
-
run_branch_gates: ReplayedGates | null
|
|
123
|
-
/** The run notes a fresh agent gets (`newestRunNotes`). */
|
|
124
|
-
run_notes: PromptRunNote[]
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const stuck = ({
|
|
128
|
-
reason,
|
|
129
|
-
detail,
|
|
130
|
-
}: {
|
|
131
|
-
reason: StuckReason
|
|
132
|
-
detail: string
|
|
133
|
-
}): FinalReviewAction => ({ type: 'mark_final_review_stuck', reason, detail })
|
|
15
|
+
export {
|
|
16
|
+
isFinalReviewAction,
|
|
17
|
+
type FinalReviewAction,
|
|
18
|
+
type FinalReviewDecision,
|
|
19
|
+
} from './final-review-action'
|
|
134
20
|
|
|
135
21
|
/** A fresh lens, or the step after its failed try. */
|
|
136
22
|
const lensStep = ({
|
|
@@ -158,185 +44,6 @@ const lensStep = ({
|
|
|
158
44
|
return failureCap({ review, failed }) ?? launch
|
|
159
45
|
}
|
|
160
46
|
|
|
161
|
-
/**
|
|
162
|
-
* The stuck step when a failed turn used up its role's tries (or engine
|
|
163
|
-
* failures in a row), else `null`: the role gets another go.
|
|
164
|
-
*/
|
|
165
|
-
const failureCap = ({
|
|
166
|
-
review,
|
|
167
|
-
failed,
|
|
168
|
-
}: {
|
|
169
|
-
review: FinalReviewState
|
|
170
|
-
failed: ReplayedAgentFailure
|
|
171
|
-
}): FinalReviewAction | null => {
|
|
172
|
-
const { role, error, failure } = failed
|
|
173
|
-
if (failure === 'engine') {
|
|
174
|
-
const count = review.engine_failures[role] ?? 0
|
|
175
|
-
return count >= MAX_ENGINE_FAILURES
|
|
176
|
-
? stuck({
|
|
177
|
-
reason: 'agent_failed',
|
|
178
|
-
detail: `The engine failed to run the ${role} ${count} times in a row: ${error}`,
|
|
179
|
-
})
|
|
180
|
-
: null
|
|
181
|
-
}
|
|
182
|
-
const tries = review.failed_tries[role] ?? 0
|
|
183
|
-
return tries >= MAX_FIX_ROUNDS
|
|
184
|
-
? stuck({
|
|
185
|
-
reason: 'agent_failed',
|
|
186
|
-
detail: `The ${role} failed ${tries} tries in the final review; the last one: ${error}`,
|
|
187
|
-
})
|
|
188
|
-
: null
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* A fresh fixer of this role, told its findings (or the failed gates). With
|
|
193
|
-
* `follow_up`, it stands in for a follow-up whose session is gone, and its
|
|
194
|
-
* prompt carries the message. A fixer taking a turn a crash cut off is told
|
|
195
|
-
* of the crash.
|
|
196
|
-
*/
|
|
197
|
-
const launchFixer = ({
|
|
198
|
-
role,
|
|
199
|
-
review,
|
|
200
|
-
snapshot,
|
|
201
|
-
run_notes,
|
|
202
|
-
follow_up,
|
|
203
|
-
}: DecideArgs & {
|
|
204
|
-
role: FinalFixerRole
|
|
205
|
-
follow_up?: string
|
|
206
|
-
}): FinalReviewAction => {
|
|
207
|
-
const { fix } = review
|
|
208
|
-
if (fix === null) throw new Error('No final review fix round is open.')
|
|
209
|
-
// An implementer with no code findings left is here for failed gates.
|
|
210
|
-
const gates =
|
|
211
|
-
role === 'implementer' && fix.code_answered ? review.gates : null
|
|
212
|
-
const sections = [
|
|
213
|
-
...(follow_up === undefined
|
|
214
|
-
? []
|
|
215
|
-
: [followUpSection({ message: follow_up })]),
|
|
216
|
-
...(review.crashed_turn === role ? [CRASH_SECTION] : []),
|
|
217
|
-
]
|
|
218
|
-
return {
|
|
219
|
-
type: 'launch_final_fixer',
|
|
220
|
-
role,
|
|
221
|
-
prompt: finalFixerPrompt({
|
|
222
|
-
role,
|
|
223
|
-
snapshot,
|
|
224
|
-
fix,
|
|
225
|
-
gates,
|
|
226
|
-
run_notes,
|
|
227
|
-
sections,
|
|
228
|
-
}),
|
|
229
|
-
may_edit_tests: role === 'test-writer',
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const isFixer = (role: AgentRole): role is FinalFixerRole =>
|
|
234
|
-
role === 'test-writer' || role === 'implementer'
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* The next step after a fixer's failed turn: an engine failure starts a
|
|
238
|
-
* fresh fixer, any other a follow-up in its session (a fresh fixer with the
|
|
239
|
-
* same message with none), until its tries run out.
|
|
240
|
-
*/
|
|
241
|
-
const failedFixerStep = ({
|
|
242
|
-
failed,
|
|
243
|
-
...args
|
|
244
|
-
}: DecideArgs & { failed: ReplayedAgentFailure }): FinalReviewAction => {
|
|
245
|
-
const cap = failureCap({ review: args.review, failed })
|
|
246
|
-
if (cap !== null) return cap
|
|
247
|
-
const { role, error, failure, session_id } = failed
|
|
248
|
-
if (!isFixer(role)) throw new Error(`${role} is no final review fixer.`)
|
|
249
|
-
if (failure === 'engine') return launchFixer({ ...args, role })
|
|
250
|
-
const message = failedTryMessage({ failure, error })
|
|
251
|
-
if (session_id === null) {
|
|
252
|
-
return launchFixer({ ...args, role, follow_up: message })
|
|
253
|
-
}
|
|
254
|
-
return {
|
|
255
|
-
type: 'follow_up_final_fixer',
|
|
256
|
-
role,
|
|
257
|
-
session_id,
|
|
258
|
-
message,
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* A final review fix round, once every due lens finished and some finding
|
|
264
|
-
* blocks: start the round, a fresh test-writer for the test findings, then
|
|
265
|
-
* a fresh implementer for the code findings, the gates (and their fix loop
|
|
266
|
-
* in the implementer's session), the fix commit, and the push. `null` once
|
|
267
|
-
* the fixes are pushed.
|
|
268
|
-
*/
|
|
269
|
-
const fixStep = (args: DecideArgs): FinalReviewAction | null => {
|
|
270
|
-
const { review, run_branch, snapshot } = args
|
|
271
|
-
const { fix } = review
|
|
272
|
-
if (fix === null) return null
|
|
273
|
-
if (fix.round > MAX_FIX_ROUNDS) {
|
|
274
|
-
return stuck({
|
|
275
|
-
reason: 'changes_requested',
|
|
276
|
-
detail: `The final review still asks for changes after ${MAX_FIX_ROUNDS} fix rounds:\n${openFinalFindingsText({ findings: fix.findings })}`,
|
|
277
|
-
})
|
|
278
|
-
}
|
|
279
|
-
if (!review.fixing_started) {
|
|
280
|
-
return { type: 'start_final_fix', round: fix.round }
|
|
281
|
-
}
|
|
282
|
-
const failed = [
|
|
283
|
-
review.agent_failures['test-writer'],
|
|
284
|
-
review.agent_failures.implementer,
|
|
285
|
-
].find((entry) => entry !== undefined)
|
|
286
|
-
if (failed !== undefined) return failedFixerStep({ ...args, failed })
|
|
287
|
-
if (!fix.tests_answered)
|
|
288
|
-
return launchFixer({ ...args, role: 'test-writer' })
|
|
289
|
-
if (!fix.code_answered) return launchFixer({ ...args, role: 'implementer' })
|
|
290
|
-
if (fix.bad_test !== null) {
|
|
291
|
-
const { file, name, reason } = fix.bad_test
|
|
292
|
-
const where = [file, name].filter(Boolean).join(' > ')
|
|
293
|
-
return stuck({
|
|
294
|
-
reason: 'bad_test',
|
|
295
|
-
detail: `While fixing the final review's findings, the implementer sent a test back as bad: ${where === '' ? reason : `${where}: ${reason}`}`,
|
|
296
|
-
})
|
|
297
|
-
}
|
|
298
|
-
const { gates } = review
|
|
299
|
-
if (gates === null) return { type: 'run_final_gates' }
|
|
300
|
-
if (!gates.ok) {
|
|
301
|
-
if (review.gate_fix_rounds >= MAX_FIX_ROUNDS) {
|
|
302
|
-
return stuck({
|
|
303
|
-
reason: 'gates_failed',
|
|
304
|
-
detail: `The gates still fail after the final review's fixes, after ${MAX_FIX_ROUNDS} fix rounds:\n${failedChecks({ gates })}`,
|
|
305
|
-
})
|
|
306
|
-
}
|
|
307
|
-
const session_id = review.sessions.implementer
|
|
308
|
-
if (session_id === undefined) {
|
|
309
|
-
return launchFixer({ ...args, role: 'implementer' })
|
|
310
|
-
}
|
|
311
|
-
return {
|
|
312
|
-
type: 'follow_up_final_fixer',
|
|
313
|
-
role: 'implementer',
|
|
314
|
-
session_id,
|
|
315
|
-
message: gateFixMessage({ gates }),
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
if (review.commit === null) {
|
|
319
|
-
const hits = review.leftovers
|
|
320
|
-
if (hits !== null && hits.length > 0) {
|
|
321
|
-
return stuck({
|
|
322
|
-
reason: 'leftovers_found',
|
|
323
|
-
detail: hits
|
|
324
|
-
.map(({ path, reason }) => `${path}: ${reason}`)
|
|
325
|
-
.join('\n'),
|
|
326
|
-
})
|
|
327
|
-
}
|
|
328
|
-
return {
|
|
329
|
-
type: 'commit_final_fix',
|
|
330
|
-
round: fix.round,
|
|
331
|
-
message: `fix: final review round ${fix.round} for spec #${snapshot.spec.number}`,
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
if (review.pushed === null) {
|
|
335
|
-
return { type: 'push_final_fixes', branch: run_branch.branch }
|
|
336
|
-
}
|
|
337
|
-
return null
|
|
338
|
-
}
|
|
339
|
-
|
|
340
47
|
/**
|
|
341
48
|
* The final review: the decision step's part between the last ticket's push
|
|
342
49
|
* and the PR. Pure. It always runs, even for a one-ticket spec.
|
|
@@ -350,8 +57,18 @@ const fixStep = (args: DecideArgs): FinalReviewAction | null => {
|
|
|
350
57
|
* the run branch, and a push. Then only the lenses that had blocking
|
|
351
58
|
* findings re-review only the new changes (from the last round's HEAD),
|
|
352
59
|
* with their earlier findings and the fixers' answers, and rule on each
|
|
353
|
-
* "won't fix".
|
|
60
|
+
* "won't fix". With a slow test command (#563), round 1 also runs the slow
|
|
61
|
+
* tests once every lens finished, and settles only once they ran; failing
|
|
62
|
+
* ones open the fix round (or join it). A fix round with no finding, opened only
|
|
63
|
+
* by them, is just its gates (which run the slow tests again) with their
|
|
64
|
+
* fix loop, the commit, and the push, with no lens re-review.
|
|
65
|
+
* A review still asking for changes after `MAX_FIX_ROUNDS` fix
|
|
354
66
|
* rounds is stuck, and so is a failed fix loop, a bad test, or a leftover.
|
|
67
|
+
* Once a fix commit was pushed and no blocking finding is left, the fresh
|
|
68
|
+
* pass (#548) has all five lenses read the whole branch again, without
|
|
69
|
+
* earlier findings; its blocking findings get one fix round (past the round
|
|
70
|
+
* cap, too) and one re-review, and what is still open then tops the PR
|
|
71
|
+
* instead of getting the review stuck.
|
|
355
72
|
* Failed tries are capped per role like a ticket's. A stuck final review
|
|
356
73
|
* waits for a reply; `ship` opens the PR anyway. A final review step cut
|
|
357
74
|
* off by a crash `MAX_CRASHES` times in a row is stuck too.
|
|
@@ -399,9 +116,37 @@ export const decideFinalReview = (args: DecideArgs): FinalReviewDecision => {
|
|
|
399
116
|
)
|
|
400
117
|
return working(stop === undefined ? steps : [stop])
|
|
401
118
|
}
|
|
402
|
-
|
|
119
|
+
// Round 1's slow tests run once its lenses are in, never beside them:
|
|
120
|
+
// a lens's after-turn guard judges the run branch's worktree, so nothing
|
|
121
|
+
// else may write there while a lens runs. The round waits for them (#563).
|
|
122
|
+
if (review.slow_due && review.slow_tests === null) {
|
|
123
|
+
return working([{ type: 'run_final_slow_tests' }])
|
|
124
|
+
}
|
|
125
|
+
// Clean after a fix commit: every lens reads the whole branch once more,
|
|
126
|
+
// fresh, before the PR opens (#548). Once per final review; with no fix
|
|
127
|
+
// commit, round 1 was already a full fresh read.
|
|
128
|
+
const ending: FinalReviewAction =
|
|
129
|
+
review.fix_pushed && !review.fresh_started
|
|
130
|
+
? {
|
|
131
|
+
type: 'start_final_review',
|
|
132
|
+
round: review.round + 1,
|
|
133
|
+
from_sha: run_branch.base_sha,
|
|
134
|
+
lenses: [...LENS_NAMES],
|
|
135
|
+
fresh: true,
|
|
136
|
+
}
|
|
137
|
+
: { type: 'pass_final_review' }
|
|
138
|
+
if (review.fix === null) return working([ending])
|
|
139
|
+
// The fresh pass's fix was re-checked: what is still open tops the PR,
|
|
140
|
+
// and the final review never gets stuck on it (#548).
|
|
141
|
+
if (review.fresh_started && !review.fresh) {
|
|
142
|
+
return working([{ type: 'pass_final_review' }])
|
|
143
|
+
}
|
|
403
144
|
const step = fixStep(args)
|
|
404
145
|
if (step !== null) return working([step])
|
|
146
|
+
// A fix round opened only by failing slow tests ends at its push: its
|
|
147
|
+
// gates ran the slow tests on what was pushed, and no lens had a finding
|
|
148
|
+
// to look at again (#563), so the fresh pass comes next.
|
|
149
|
+
if (review.fix.findings.length === 0) return working([ending])
|
|
405
150
|
// The fixes are pushed: the lenses with blocking findings look again.
|
|
406
151
|
return working([
|
|
407
152
|
{
|