@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,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One agent turn (`runTurn`): the worktree guard around it, and how it
|
|
3
|
+
* ended, journaled. Also the `launch_agent` and `follow_up_agent` steps,
|
|
4
|
+
* which start a turn.
|
|
5
|
+
*/
|
|
6
|
+
import { need, ticketWorktree, type BuildContext } from './build-context'
|
|
7
|
+
import { mayEditTests, type BuildAction } from './decide-build'
|
|
8
|
+
import { doChores } from './engine-chores'
|
|
9
|
+
import { closeSessions } from './session-close'
|
|
10
|
+
|
|
11
|
+
import type { AgentTurn } from '../agents/agent-launcher'
|
|
12
|
+
import {
|
|
13
|
+
parseRoleResult,
|
|
14
|
+
type AgentRole,
|
|
15
|
+
type RoleResult,
|
|
16
|
+
} from '../agents/role-results'
|
|
17
|
+
import { DEFAULT_MODEL } from '../config/engine-config'
|
|
18
|
+
import { describeViolations } from '../guards/after-turn-check'
|
|
19
|
+
import { guardRoleOf } from '../guards/role-rules'
|
|
20
|
+
import { enforceAfterTurn, snapshotWorktree } from '../guards/worktree-state'
|
|
21
|
+
import { launchPick, type ModelReason } from '../jev/model-pick'
|
|
22
|
+
import type { AgentFailure, JournalRecord } from '../journal/journal-record'
|
|
23
|
+
import type { ReplayedWorktree } from '../journal/replay'
|
|
24
|
+
import { sessionSignal } from '../limits/plan-signals'
|
|
25
|
+
import { createAgentMessaging } from '../messages/agent-messaging'
|
|
26
|
+
|
|
27
|
+
const failTurn = ({
|
|
28
|
+
context,
|
|
29
|
+
ticket,
|
|
30
|
+
role,
|
|
31
|
+
error,
|
|
32
|
+
failure,
|
|
33
|
+
session_id,
|
|
34
|
+
session_gone,
|
|
35
|
+
out_of_turns,
|
|
36
|
+
}: {
|
|
37
|
+
context: BuildContext
|
|
38
|
+
ticket: number | null
|
|
39
|
+
role: AgentRole
|
|
40
|
+
error: string
|
|
41
|
+
failure: AgentFailure
|
|
42
|
+
session_id: string | null
|
|
43
|
+
session_gone: boolean
|
|
44
|
+
out_of_turns: boolean
|
|
45
|
+
}) => {
|
|
46
|
+
context.journal.append({
|
|
47
|
+
kind: 'agent_failed',
|
|
48
|
+
ticket,
|
|
49
|
+
role,
|
|
50
|
+
content: {
|
|
51
|
+
role,
|
|
52
|
+
error,
|
|
53
|
+
failure,
|
|
54
|
+
session_id,
|
|
55
|
+
...(session_gone ? { session_gone } : {}),
|
|
56
|
+
...(out_of_turns ? { out_of_turns } : {}),
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Runs one agent turn, a launch or a follow-up, and journals how it ended.
|
|
63
|
+
* Before the turn the engine snapshots the worktree; after it, the
|
|
64
|
+
* after-turn check undoes and reports anything the role may not change, for
|
|
65
|
+
* every launcher alike; what others changed in the shared `.git` meanwhile
|
|
66
|
+
* is journaled as `shared_git_changed`. Then the result is judged by its
|
|
67
|
+
* structured output. Each failed turn is journaled once as `agent_failed`
|
|
68
|
+
* with how it failed; the decision step picks what happens next. A writer's
|
|
69
|
+
* result then gets its chores done (`doChores`, #618); one the engine could
|
|
70
|
+
* not do fails the turn as a `chore` failure. A launcher stop journals
|
|
71
|
+
* `run_stopped` and ends the run. A turn the plan cut off (a rejected limit,
|
|
72
|
+
* overage, a billing error) journals only its session. Both close the
|
|
73
|
+
* turn's session first, since no follow-up can reach it.
|
|
74
|
+
*
|
|
75
|
+
* @param worktree - Where the agent works: its ticket's worktree, or the run
|
|
76
|
+
* branch's for the final review.
|
|
77
|
+
* @param ticket - The ticket its records name, `null` for the final review.
|
|
78
|
+
* @returns The role's result when the agent finished, else `null`.
|
|
79
|
+
*/
|
|
80
|
+
export const runTurn = async ({
|
|
81
|
+
context,
|
|
82
|
+
worktree,
|
|
83
|
+
ticket,
|
|
84
|
+
role,
|
|
85
|
+
may_edit_tests,
|
|
86
|
+
start,
|
|
87
|
+
}: {
|
|
88
|
+
context: BuildContext
|
|
89
|
+
worktree: ReplayedWorktree
|
|
90
|
+
ticket: number | null
|
|
91
|
+
role: AgentRole
|
|
92
|
+
may_edit_tests: boolean
|
|
93
|
+
start: (cwd: string) => Promise<AgentTurn>
|
|
94
|
+
}): Promise<RoleResult | null> => {
|
|
95
|
+
const { path, branch } = worktree
|
|
96
|
+
const before = await snapshotWorktree({
|
|
97
|
+
cwd: path,
|
|
98
|
+
branch,
|
|
99
|
+
config: context.config,
|
|
100
|
+
})
|
|
101
|
+
const turn = await start(path)
|
|
102
|
+
if (turn.session !== undefined) {
|
|
103
|
+
context.journal.append({
|
|
104
|
+
kind: 'agent_session',
|
|
105
|
+
ticket,
|
|
106
|
+
role,
|
|
107
|
+
content: { role, session: turn.session },
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
const guard_role = guardRoleOf({ role })
|
|
111
|
+
const { violations, outside } = await enforceAfterTurn({
|
|
112
|
+
cwd: path,
|
|
113
|
+
branch,
|
|
114
|
+
role: guard_role,
|
|
115
|
+
may_edit_tests,
|
|
116
|
+
config: context.config,
|
|
117
|
+
before,
|
|
118
|
+
leave_out: context.state.prepare_made,
|
|
119
|
+
})
|
|
120
|
+
// Others' changes to the shared .git: noted, never blamed or undone.
|
|
121
|
+
if (outside.length > 0) {
|
|
122
|
+
context.journal.append({
|
|
123
|
+
kind: 'shared_git_changed',
|
|
124
|
+
ticket,
|
|
125
|
+
role,
|
|
126
|
+
content: { role, changes: outside },
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
// The plan cut the turn off. Its session, journaled above, holds why,
|
|
130
|
+
// and the decision step reads it from there: a limit wait or a billing
|
|
131
|
+
// stop. The turn uses up no try, and its step is taken again. With no
|
|
132
|
+
// sign in the session, the engine can't tell which, so it stops.
|
|
133
|
+
const unexplained =
|
|
134
|
+
!turn.ok &&
|
|
135
|
+
turn.failure === 'plan' &&
|
|
136
|
+
(turn.session === undefined ||
|
|
137
|
+
sessionSignal({ session: turn.session }).kind === 'ok')
|
|
138
|
+
// Either way no follow-up reaches the session: close it now.
|
|
139
|
+
const cut = !turn.ok && (turn.failure === 'plan' || turn.failure === 'stop')
|
|
140
|
+
if (cut && turn.session_id !== undefined) {
|
|
141
|
+
await closeSessions({
|
|
142
|
+
journal: context.journal,
|
|
143
|
+
launcher: context.launcher,
|
|
144
|
+
sessions: [{ session_id: turn.session_id, ticket, role }],
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
if (!turn.ok && turn.failure === 'plan' && !unexplained) return null
|
|
148
|
+
if (!turn.ok && (turn.failure === 'stop' || unexplained)) {
|
|
149
|
+
context.journal.append({
|
|
150
|
+
kind: 'run_stopped',
|
|
151
|
+
ticket,
|
|
152
|
+
role,
|
|
153
|
+
content: { reason: turn.error, role, billing: false },
|
|
154
|
+
})
|
|
155
|
+
throw new Error(`Run stopped: ${turn.error}`)
|
|
156
|
+
}
|
|
157
|
+
const session_id = turn.session_id ?? null
|
|
158
|
+
const fail = ({
|
|
159
|
+
failure,
|
|
160
|
+
error,
|
|
161
|
+
session_gone = false,
|
|
162
|
+
out_of_turns = false,
|
|
163
|
+
}: {
|
|
164
|
+
failure: AgentFailure
|
|
165
|
+
error: string
|
|
166
|
+
session_gone?: boolean
|
|
167
|
+
out_of_turns?: boolean
|
|
168
|
+
}) => {
|
|
169
|
+
failTurn({
|
|
170
|
+
context,
|
|
171
|
+
ticket,
|
|
172
|
+
role,
|
|
173
|
+
error,
|
|
174
|
+
failure,
|
|
175
|
+
session_id,
|
|
176
|
+
session_gone,
|
|
177
|
+
out_of_turns,
|
|
178
|
+
})
|
|
179
|
+
return null
|
|
180
|
+
}
|
|
181
|
+
if (violations.length > 0) {
|
|
182
|
+
return fail({
|
|
183
|
+
failure: 'guard',
|
|
184
|
+
error: describeViolations({ role: guard_role, violations }),
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
if (!turn.ok) {
|
|
188
|
+
return fail({
|
|
189
|
+
failure: turn.failure === 'engine' ? 'engine' : 'agent',
|
|
190
|
+
error: turn.error,
|
|
191
|
+
session_gone: turn.session_gone === true,
|
|
192
|
+
out_of_turns: turn.out_of_turns === true,
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
if (
|
|
196
|
+
turn.structured_output === undefined ||
|
|
197
|
+
turn.structured_output === null
|
|
198
|
+
) {
|
|
199
|
+
return fail({
|
|
200
|
+
failure: 'result',
|
|
201
|
+
error: `The ${role} finished with no structured output.`,
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
const checked = parseRoleResult({ role, output: turn.structured_output })
|
|
205
|
+
if (!checked.ok) return fail({ failure: 'result', error: checked.error })
|
|
206
|
+
// The chores the writer asked for (#618), before any check or commit.
|
|
207
|
+
const problems = await doChores({
|
|
208
|
+
context,
|
|
209
|
+
cwd: path,
|
|
210
|
+
ticket,
|
|
211
|
+
result: checked.value,
|
|
212
|
+
guard: { role: guard_role, may_edit_tests },
|
|
213
|
+
})
|
|
214
|
+
if (problems.length > 0) {
|
|
215
|
+
return fail({
|
|
216
|
+
failure: 'chore',
|
|
217
|
+
error: problems.map((line) => `- ${line}`).join('\n'),
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
context.journal.append({
|
|
221
|
+
kind: 'agent_finished',
|
|
222
|
+
ticket,
|
|
223
|
+
role,
|
|
224
|
+
content: { ...checked.value, session_id },
|
|
225
|
+
})
|
|
226
|
+
return checked.value
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** The session a record names, if any. */
|
|
230
|
+
const sessionOf = (record: JournalRecord): string | null => {
|
|
231
|
+
switch (record.kind) {
|
|
232
|
+
case 'agent_session':
|
|
233
|
+
return record.content.session.session_id
|
|
234
|
+
case 'agent_finished':
|
|
235
|
+
case 'agent_failed':
|
|
236
|
+
return record.content.session_id
|
|
237
|
+
default:
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** The model a session launched on, and why (`null` before #603). */
|
|
243
|
+
export type SessionModel = { model: string; model_reason: ModelReason | null }
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The model a session launched on, and why, from the journal: the
|
|
247
|
+
* `agent_started` of the launch the session's first record came after (same
|
|
248
|
+
* ticket and role), or of an earlier follow-up to it. `null` if the journal
|
|
249
|
+
* names none, as in journals from before #600; every agent ran Opus then.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* sessionModel({ records: journal.read(), session_id }) // { model: 'opus', model_reason: 'locked' }
|
|
253
|
+
*/
|
|
254
|
+
export const sessionModel = ({
|
|
255
|
+
records,
|
|
256
|
+
session_id,
|
|
257
|
+
}: {
|
|
258
|
+
records: JournalRecord[]
|
|
259
|
+
session_id: string
|
|
260
|
+
}): SessionModel | null => {
|
|
261
|
+
const followed = records.findLast(
|
|
262
|
+
(record) =>
|
|
263
|
+
record.kind === 'agent_started' &&
|
|
264
|
+
record.content.follow_up_of === session_id &&
|
|
265
|
+
record.content.model !== null
|
|
266
|
+
)
|
|
267
|
+
const first = records.findIndex(
|
|
268
|
+
(record) => sessionOf(record) === session_id
|
|
269
|
+
)
|
|
270
|
+
const { ticket, role } = records[first] ?? {}
|
|
271
|
+
const started =
|
|
272
|
+
followed ??
|
|
273
|
+
(first === -1
|
|
274
|
+
? undefined
|
|
275
|
+
: records
|
|
276
|
+
.slice(0, first)
|
|
277
|
+
.findLast(
|
|
278
|
+
(record) =>
|
|
279
|
+
record.kind === 'agent_started' &&
|
|
280
|
+
record.ticket === ticket &&
|
|
281
|
+
record.role === role &&
|
|
282
|
+
record.content.follow_up_of === null
|
|
283
|
+
))
|
|
284
|
+
if (started?.kind !== 'agent_started') return null
|
|
285
|
+
const { model, model_reason } = started.content
|
|
286
|
+
return model === null ? null : { model, model_reason }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** A follow-up's model: its session's, else Opus, as before #600. */
|
|
290
|
+
export const followUpModel = ({
|
|
291
|
+
records,
|
|
292
|
+
session_id,
|
|
293
|
+
}: {
|
|
294
|
+
records: JournalRecord[]
|
|
295
|
+
session_id: string
|
|
296
|
+
}): SessionModel =>
|
|
297
|
+
sessionModel({ records, session_id }) ?? {
|
|
298
|
+
model: DEFAULT_MODEL,
|
|
299
|
+
model_reason: null,
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export const launchAgent = async ({
|
|
303
|
+
context,
|
|
304
|
+
action,
|
|
305
|
+
}: {
|
|
306
|
+
context: BuildContext
|
|
307
|
+
action: Extract<BuildAction, { type: 'launch_agent' }>
|
|
308
|
+
}) => {
|
|
309
|
+
const { ticket, role, prompt, may_edit_tests } = action
|
|
310
|
+
// The decision model's answer, journaled just before this step, picks
|
|
311
|
+
// a routed role's model; a reviewer is locked on Opus. A redo keeps
|
|
312
|
+
// the model its first try journaled.
|
|
313
|
+
const { model, model_reason } = launchPick({
|
|
314
|
+
records: context.journal.read(),
|
|
315
|
+
state: context.state,
|
|
316
|
+
config: context.config,
|
|
317
|
+
ticket,
|
|
318
|
+
role,
|
|
319
|
+
step: context.step,
|
|
320
|
+
})
|
|
321
|
+
context.journal.append({
|
|
322
|
+
kind: 'agent_started',
|
|
323
|
+
ticket,
|
|
324
|
+
role,
|
|
325
|
+
content: { role, prompt, follow_up_of: null, model, model_reason },
|
|
326
|
+
})
|
|
327
|
+
await runTurn({
|
|
328
|
+
context,
|
|
329
|
+
worktree: ticketWorktree({ state: context.state, ticket }),
|
|
330
|
+
ticket,
|
|
331
|
+
role,
|
|
332
|
+
may_edit_tests,
|
|
333
|
+
start: (cwd) =>
|
|
334
|
+
context.launcher.launch({
|
|
335
|
+
role,
|
|
336
|
+
ticket,
|
|
337
|
+
prompt,
|
|
338
|
+
cwd,
|
|
339
|
+
may_edit_tests,
|
|
340
|
+
config: context.config,
|
|
341
|
+
messaging: createAgentMessaging({
|
|
342
|
+
journal: context.journal,
|
|
343
|
+
ticket,
|
|
344
|
+
role,
|
|
345
|
+
}),
|
|
346
|
+
model,
|
|
347
|
+
}),
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export const followUpAgent = async ({
|
|
352
|
+
context,
|
|
353
|
+
action,
|
|
354
|
+
}: {
|
|
355
|
+
context: BuildContext
|
|
356
|
+
action: Extract<BuildAction, { type: 'follow_up_agent' }>
|
|
357
|
+
}) => {
|
|
358
|
+
const { ticket, role, session_id, message } = action
|
|
359
|
+
const snapshot = need({
|
|
360
|
+
value: context.state.snapshot?.tickets[ticket],
|
|
361
|
+
what: `snapshot of #${ticket}`,
|
|
362
|
+
})
|
|
363
|
+
const { model, model_reason } = followUpModel({
|
|
364
|
+
records: context.journal.read(),
|
|
365
|
+
session_id,
|
|
366
|
+
})
|
|
367
|
+
context.journal.append({
|
|
368
|
+
kind: 'agent_started',
|
|
369
|
+
ticket,
|
|
370
|
+
role,
|
|
371
|
+
content: {
|
|
372
|
+
role,
|
|
373
|
+
prompt: message,
|
|
374
|
+
follow_up_of: session_id,
|
|
375
|
+
model,
|
|
376
|
+
model_reason,
|
|
377
|
+
},
|
|
378
|
+
})
|
|
379
|
+
await runTurn({
|
|
380
|
+
context,
|
|
381
|
+
worktree: ticketWorktree({ state: context.state, ticket }),
|
|
382
|
+
ticket,
|
|
383
|
+
role,
|
|
384
|
+
may_edit_tests: mayEditTests({ role, ticket: snapshot }),
|
|
385
|
+
start: (cwd) =>
|
|
386
|
+
context.launcher.followUp({
|
|
387
|
+
session_id,
|
|
388
|
+
role,
|
|
389
|
+
ticket,
|
|
390
|
+
message,
|
|
391
|
+
cwd,
|
|
392
|
+
config: context.config,
|
|
393
|
+
}),
|
|
394
|
+
})
|
|
395
|
+
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
DoneCommit,
|
|
4
4
|
TestWriterResult,
|
|
5
5
|
} from '../agents/role-results'
|
|
6
|
-
import type { TicketSnapshot } from '../intake/intake-schemas'
|
|
6
|
+
import type { LeftOutTicket, TicketSnapshot } from '../intake/intake-schemas'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A ticket whose work is already on the base branch (#484): its
|
|
@@ -30,13 +30,56 @@ export type AlreadyDoneAction =
|
|
|
30
30
|
/**
|
|
31
31
|
* End the run with nothing to do: at intake (`already_done` is empty),
|
|
32
32
|
* or once every ticket's work was already done and those are closed.
|
|
33
|
+
* At intake, `left_out` names the open tickets left for a person (#499).
|
|
33
34
|
*/
|
|
34
35
|
| {
|
|
35
36
|
type: 'finish_nothing_to_do'
|
|
36
37
|
closed_tickets: number[]
|
|
37
38
|
already_done: number[]
|
|
39
|
+
left_out?: LeftOutTicket[]
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Check a test-writer's `already_done` evidence (#495) in the ticket's
|
|
44
|
+
* worktree before the ticket counts as done: each commit in `done_by` is on
|
|
45
|
+
* `base_sha` (the commit the worktree started from), every criterion names
|
|
46
|
+
* a test, and each named test exists and passes there. The engine journals
|
|
47
|
+
* the check as `already_done_checked`.
|
|
48
|
+
*/
|
|
49
|
+
export type CheckAlreadyDoneAction = {
|
|
50
|
+
type: 'check_already_done'
|
|
51
|
+
ticket: number
|
|
52
|
+
base_sha: string
|
|
53
|
+
criteria_ids: string[]
|
|
54
|
+
done_by: DoneCommit[]
|
|
55
|
+
criteria: CriterionTests[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The step that checks a test-writer's `already_done` evidence (#495), on
|
|
60
|
+
* the commit the ticket's worktree started from.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* checkAlreadyDoneStep({ ticket, base_sha: 'b0', result })
|
|
64
|
+
* // { type: 'check_already_done', ticket: 11, base_sha: 'b0', criteria_ids: ['AC1'], done_by, criteria }
|
|
65
|
+
*/
|
|
66
|
+
export const checkAlreadyDoneStep = ({
|
|
67
|
+
ticket,
|
|
68
|
+
base_sha,
|
|
69
|
+
result,
|
|
70
|
+
}: {
|
|
71
|
+
ticket: TicketSnapshot
|
|
72
|
+
base_sha: string
|
|
73
|
+
result: TestWriterResult
|
|
74
|
+
}): CheckAlreadyDoneAction => ({
|
|
75
|
+
type: 'check_already_done',
|
|
76
|
+
ticket: ticket.number,
|
|
77
|
+
base_sha,
|
|
78
|
+
criteria_ids: ticket.criteria.map(({ id }) => id),
|
|
79
|
+
done_by: result.done_by,
|
|
80
|
+
criteria: result.criteria,
|
|
81
|
+
})
|
|
82
|
+
|
|
40
83
|
/** How many characters of a sha the engine shows. */
|
|
41
84
|
const SHORT_SHA = 7
|
|
42
85
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The build step's actions (`BuildAction`), and what every ticket step
|
|
3
|
+
* shares: its arguments, a stuck action, and whether a ticket is a refactor.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AlreadyDoneAction, CheckAlreadyDoneAction } from './already-done'
|
|
7
|
+
import type { FinalReviewAction } from './decide-final-review'
|
|
8
|
+
import type { StuckAction } from './decide-stuck'
|
|
9
|
+
|
|
10
|
+
import type { AgentRole, BadTest, CriterionTests } from '../agents/role-results'
|
|
11
|
+
import type { TicketSnapshot } from '../intake/intake-schemas'
|
|
12
|
+
import type {
|
|
13
|
+
ChangesetBump,
|
|
14
|
+
CommitStage,
|
|
15
|
+
GateTarget,
|
|
16
|
+
RejoinCause,
|
|
17
|
+
StuckReason,
|
|
18
|
+
} from '../journal/journal-record'
|
|
19
|
+
import type {
|
|
20
|
+
ReplayedRunNote,
|
|
21
|
+
ReplayedSnapshot,
|
|
22
|
+
TicketProgress,
|
|
23
|
+
} from '../journal/replay'
|
|
24
|
+
import { REFACTOR_LABEL } from '../tracker/tracker'
|
|
25
|
+
|
|
26
|
+
/** The next build step for a run whose intake passed. */
|
|
27
|
+
export type BuildAction =
|
|
28
|
+
/** Make the run branch, in its own worktree, from the base branch. */
|
|
29
|
+
| { type: 'create_run_branch'; spec_number: number; base_branch: string }
|
|
30
|
+
/** Make the ticket's worktree on a new branch from the run branch. */
|
|
31
|
+
| { type: 'create_ticket_worktree'; ticket: number; run_branch: string }
|
|
32
|
+
/**
|
|
33
|
+
* Install the dependencies from the lockfile, without changing it, in a
|
|
34
|
+
* new ticket worktree or the run branch's checkout (`ticket` is `null`),
|
|
35
|
+
* before any agent or gate runs there.
|
|
36
|
+
*/
|
|
37
|
+
| {
|
|
38
|
+
type: 'install_dependencies'
|
|
39
|
+
target: GateTarget
|
|
40
|
+
ticket: number | null
|
|
41
|
+
}
|
|
42
|
+
/** Run the tests before any agent works, to know the old tests. */
|
|
43
|
+
| { type: 'run_baseline_tests'; ticket: number }
|
|
44
|
+
/**
|
|
45
|
+
* Take `from_ticket`'s baseline instead of running the tests again: both
|
|
46
|
+
* worktrees start from the same run-branch commit, `base_sha`.
|
|
47
|
+
*/
|
|
48
|
+
| {
|
|
49
|
+
type: 'reuse_baseline_tests'
|
|
50
|
+
ticket: number
|
|
51
|
+
from_ticket: number
|
|
52
|
+
base_sha: string
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Start a fresh agent session in the ticket's worktree with this prompt.
|
|
56
|
+
* `may_edit_tests` is true for the test-writer and for a refactor
|
|
57
|
+
* ticket's implementer; the launcher's guards enforce it.
|
|
58
|
+
*/
|
|
59
|
+
| {
|
|
60
|
+
type: 'launch_agent'
|
|
61
|
+
ticket: number
|
|
62
|
+
role: AgentRole
|
|
63
|
+
prompt: string
|
|
64
|
+
may_edit_tests: boolean
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Send a follow-up message, such as a failed check's output, to the agent
|
|
68
|
+
* session that is still open, in a fix loop.
|
|
69
|
+
*/
|
|
70
|
+
| {
|
|
71
|
+
type: 'follow_up_agent'
|
|
72
|
+
ticket: number
|
|
73
|
+
role: AgentRole
|
|
74
|
+
session_id: string
|
|
75
|
+
message: string
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Throw away every uncommitted change in the ticket's worktree, so a
|
|
79
|
+
* fresh test-writer starts clean after a bad test. With a `bad_test`, the
|
|
80
|
+
* engine itself found the test checks nothing (#581); without one, the
|
|
81
|
+
* implementer sent it back.
|
|
82
|
+
*/
|
|
83
|
+
| { type: 'reset_ticket_worktree'; ticket: number; bad_test?: BadTest }
|
|
84
|
+
/**
|
|
85
|
+
* Prove every criterion has a test, new tests fail, old tests pass. A
|
|
86
|
+
* lone rerun (`exclusive`, #544) runs its tests alone.
|
|
87
|
+
*/
|
|
88
|
+
| {
|
|
89
|
+
type: 'run_red_check'
|
|
90
|
+
ticket: number
|
|
91
|
+
criteria_ids: string[]
|
|
92
|
+
mapping: CriterionTests[]
|
|
93
|
+
exclusive?: true
|
|
94
|
+
}
|
|
95
|
+
/** Scan for leftovers, then commit everything in the worktree. */
|
|
96
|
+
| {
|
|
97
|
+
type: 'commit_ticket'
|
|
98
|
+
ticket: number
|
|
99
|
+
stage: CommitStage
|
|
100
|
+
message: string
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Run the engine config's gates in the ticket worktree or on the run
|
|
104
|
+
* branch. A lone rerun (`exclusive`, #544) runs its tests alone.
|
|
105
|
+
*/
|
|
106
|
+
| {
|
|
107
|
+
type: 'run_gates'
|
|
108
|
+
ticket: number
|
|
109
|
+
target: GateTarget
|
|
110
|
+
exclusive?: true
|
|
111
|
+
}
|
|
112
|
+
/** Replay the approved ticket's commits onto the run branch. */
|
|
113
|
+
| { type: 'join_run_branch'; ticket: number }
|
|
114
|
+
/**
|
|
115
|
+
* Put a joined ticket's whole change back on top of the run branch, as
|
|
116
|
+
* uncommitted changes in its worktree, after a clash or failed gates
|
|
117
|
+
* after joining. With `undo_first_sha`, first undo the join on the run
|
|
118
|
+
* branch, back to before that commit.
|
|
119
|
+
*/
|
|
120
|
+
| {
|
|
121
|
+
type: 'rebase_ticket'
|
|
122
|
+
ticket: number
|
|
123
|
+
cause: RejoinCause
|
|
124
|
+
undo_first_sha: string | null
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* After a rebase, send the test the implementer called bad back to the
|
|
128
|
+
* ticket's test-writer to update (#489), keeping the implementer's code.
|
|
129
|
+
* The engine looks up what joined the run branch between `from_sha` and
|
|
130
|
+
* `base_sha` and journals it all as `tests_sent_back`.
|
|
131
|
+
*/
|
|
132
|
+
| {
|
|
133
|
+
type: 'send_tests_back'
|
|
134
|
+
ticket: number
|
|
135
|
+
round: number
|
|
136
|
+
bad_test: BadTest
|
|
137
|
+
from_sha: string
|
|
138
|
+
base_sha: string
|
|
139
|
+
}
|
|
140
|
+
/** Remove these git worktrees at the end of the run; branches stay. */
|
|
141
|
+
| { type: 'remove_worktrees'; paths: string[] }
|
|
142
|
+
/** No ticket can move, and not every ticket has joined. */
|
|
143
|
+
| { type: 'invalid_journal'; reason: string }
|
|
144
|
+
/** Push the run branch to `origin`. */
|
|
145
|
+
| { type: 'push_run_branch'; ticket: number; branch: string }
|
|
146
|
+
/** The engine can't safely pick this ticket's next step by itself. */
|
|
147
|
+
| {
|
|
148
|
+
type: 'mark_stuck'
|
|
149
|
+
ticket: number
|
|
150
|
+
reason: StuckReason
|
|
151
|
+
detail: string
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* In a repo with changesets, commit the run's one changeset to the run
|
|
155
|
+
* branch and push it, before the PR (#461).
|
|
156
|
+
*/
|
|
157
|
+
| {
|
|
158
|
+
type: 'write_changeset'
|
|
159
|
+
branch: string
|
|
160
|
+
bump: ChangesetBump
|
|
161
|
+
summary: string
|
|
162
|
+
message: string
|
|
163
|
+
}
|
|
164
|
+
/** Open the run's one pull request from the run branch. */
|
|
165
|
+
| {
|
|
166
|
+
type: 'open_pull_request'
|
|
167
|
+
head: string
|
|
168
|
+
base: string
|
|
169
|
+
title: string
|
|
170
|
+
body: string
|
|
171
|
+
}
|
|
172
|
+
/** Every ticket joined (or was skipped) and the PR is open. */
|
|
173
|
+
| {
|
|
174
|
+
type: 'done'
|
|
175
|
+
outcome: 'pr_opened'
|
|
176
|
+
pull_request: { number: number; url: string }
|
|
177
|
+
}
|
|
178
|
+
/** The final review of the whole run branch (`decide-final-review.ts`). */
|
|
179
|
+
| FinalReviewAction
|
|
180
|
+
/** The owner replied `stop`: the run ends without a PR; its branch stays. */
|
|
181
|
+
| { type: 'done'; outcome: 'stopped_by_user' }
|
|
182
|
+
/** Every ticket was skipped, so there is nothing to open a PR for. */
|
|
183
|
+
| { type: 'done'; outcome: 'all_skipped' }
|
|
184
|
+
/** Telling the owner about stuck work, and acting on their replies. */
|
|
185
|
+
| StuckAction
|
|
186
|
+
/** A ticket whose work was already on the base branch (#484). */
|
|
187
|
+
| AlreadyDoneAction
|
|
188
|
+
/** Check a test-writer's evidence that the work is already done (#495). */
|
|
189
|
+
| CheckAlreadyDoneAction
|
|
190
|
+
|
|
191
|
+
/** Whether a ticket is a refactor ticket: it skips the test-writer and red check. */
|
|
192
|
+
export const isRefactorTicket = ({
|
|
193
|
+
ticket,
|
|
194
|
+
}: {
|
|
195
|
+
ticket: TicketSnapshot
|
|
196
|
+
}): boolean => ticket.labels.includes(REFACTOR_LABEL)
|
|
197
|
+
|
|
198
|
+
export type StepArgs = {
|
|
199
|
+
snapshot: ReplayedSnapshot
|
|
200
|
+
ticket: TicketSnapshot
|
|
201
|
+
progress: TicketProgress
|
|
202
|
+
/** The run notes a fresh agent gets (`newestRunNotes`). */
|
|
203
|
+
run_notes: ReplayedRunNote[]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export const stuck = ({
|
|
207
|
+
ticket,
|
|
208
|
+
reason,
|
|
209
|
+
detail,
|
|
210
|
+
}: {
|
|
211
|
+
ticket: number
|
|
212
|
+
reason: StuckReason
|
|
213
|
+
detail: string
|
|
214
|
+
}): BuildAction => ({ type: 'mark_stuck', ticket, reason, detail })
|