@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carries out the repo wait (#615): a run started while an earlier run on
|
|
3
|
+
* the same repo is going waits for it, before intake.
|
|
4
|
+
*/
|
|
5
|
+
import { basename, dirname } from 'node:path'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
earlierRuns,
|
|
9
|
+
REPO_WAIT_NAP_MS,
|
|
10
|
+
type RepoWaitAction,
|
|
11
|
+
type RepoWaitRun,
|
|
12
|
+
} from './decide-repo-wait'
|
|
13
|
+
import { repoBusyDetail, repoWaitComment } from './repo-wait-text'
|
|
14
|
+
|
|
15
|
+
import type { goingRuns } from '../cli/going-runs'
|
|
16
|
+
import type { Journal } from '../journal/journal'
|
|
17
|
+
import type { EngineClock } from '../limits/limit-wait'
|
|
18
|
+
import { postCommentOnce, type CommentStep } from '../tracker/post-comment-once'
|
|
19
|
+
import type { Tracker } from '../tracker/tracker'
|
|
20
|
+
|
|
21
|
+
/** Lists the unfinished runs on this computer, as `goingRuns` does. */
|
|
22
|
+
export type GoingRunsReader = () => ReturnType<typeof goingRuns>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The earlier runs going on this run's repo (see `earlierRuns`), by
|
|
26
|
+
* `going_runs`. None when the board's registry can't be read: the wait
|
|
27
|
+
* never holds a run up on a list it can't trust. `ps_error` says why `ps`
|
|
28
|
+
* failed, when it did: then every unfinished run counts as going (#624).
|
|
29
|
+
*/
|
|
30
|
+
export const readEarlierRuns = async ({
|
|
31
|
+
going_runs,
|
|
32
|
+
journal,
|
|
33
|
+
}: {
|
|
34
|
+
going_runs: GoingRunsReader
|
|
35
|
+
journal: Journal
|
|
36
|
+
}): Promise<{ runs: RepoWaitRun[]; ps_error: string | null }> => {
|
|
37
|
+
const listing = await going_runs()
|
|
38
|
+
if (!listing.ok) return { runs: [], ps_error: null }
|
|
39
|
+
return {
|
|
40
|
+
runs: earlierRuns({
|
|
41
|
+
runs: listing.runs,
|
|
42
|
+
run_id: basename(dirname(journal.file)),
|
|
43
|
+
records: journal.read(),
|
|
44
|
+
}),
|
|
45
|
+
ps_error: listing.ps_error,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Carries out a repo wait. Its start tells the spec issue once which runs
|
|
51
|
+
* it waits for, then is journaled. The wait reads the going runs, then
|
|
52
|
+
* naps by `clock` and reads them again, until no earlier run on the repo is
|
|
53
|
+
* going (`repo_wait_ended`) or `until` comes (the run is stuck as
|
|
54
|
+
* `repo_busy`). Without `going_runs`, none is going. A `ps` that fails is
|
|
55
|
+
* journaled once (`repo_wait_ps_failed`), and the wait goes on.
|
|
56
|
+
*/
|
|
57
|
+
export const executeRepoWaitAction = async ({
|
|
58
|
+
action,
|
|
59
|
+
journal,
|
|
60
|
+
tracker,
|
|
61
|
+
clock,
|
|
62
|
+
going_runs,
|
|
63
|
+
step,
|
|
64
|
+
}: {
|
|
65
|
+
action: RepoWaitAction
|
|
66
|
+
journal: Journal
|
|
67
|
+
tracker: Tracker
|
|
68
|
+
clock: EngineClock
|
|
69
|
+
going_runs: GoingRunsReader | undefined
|
|
70
|
+
step: CommentStep
|
|
71
|
+
}): Promise<void> => {
|
|
72
|
+
if (action.type === 'start_repo_wait') {
|
|
73
|
+
await postCommentOnce({
|
|
74
|
+
tracker,
|
|
75
|
+
number: action.spec_number,
|
|
76
|
+
body: repoWaitComment({ runs: action.runs }),
|
|
77
|
+
step,
|
|
78
|
+
n: 0,
|
|
79
|
+
})
|
|
80
|
+
journal.append({
|
|
81
|
+
kind: 'repo_wait_started',
|
|
82
|
+
ticket: null,
|
|
83
|
+
role: null,
|
|
84
|
+
content: { runs: action.runs },
|
|
85
|
+
})
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
const end = Date.parse(action.until)
|
|
89
|
+
let ps_error: string | null = null
|
|
90
|
+
for (;;) {
|
|
91
|
+
const read =
|
|
92
|
+
going_runs === undefined
|
|
93
|
+
? { runs: [], ps_error: null }
|
|
94
|
+
: await readEarlierRuns({ going_runs, journal })
|
|
95
|
+
const { runs } = read
|
|
96
|
+
if (read.ps_error !== null && ps_error === null) {
|
|
97
|
+
ps_error = read.ps_error
|
|
98
|
+
journal.append({
|
|
99
|
+
kind: 'repo_wait_ps_failed',
|
|
100
|
+
ticket: null,
|
|
101
|
+
role: null,
|
|
102
|
+
content: { error: ps_error },
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
if (runs.length === 0) {
|
|
106
|
+
journal.append({
|
|
107
|
+
kind: 'repo_wait_ended',
|
|
108
|
+
ticket: null,
|
|
109
|
+
role: null,
|
|
110
|
+
content: {},
|
|
111
|
+
})
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
const left = end - clock.now()
|
|
115
|
+
if (left <= 0) {
|
|
116
|
+
journal.append({
|
|
117
|
+
kind: 'run_stuck',
|
|
118
|
+
ticket: null,
|
|
119
|
+
role: null,
|
|
120
|
+
content: {
|
|
121
|
+
reason: 'repo_busy',
|
|
122
|
+
detail: repoBusyDetail({ runs, ps_error }),
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
await clock.sleep(Math.min(left, REPO_WAIT_NAP_MS))
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -3,7 +3,6 @@ import sortBy from 'lodash/sortBy'
|
|
|
3
3
|
import type { RunBudgetAction } from './decide-run-budget'
|
|
4
4
|
import type { StuckAction } from './decide-stuck'
|
|
5
5
|
import { need, ticketWorktree, type BuildContext } from './execute-build'
|
|
6
|
-
import { shipFinalReview } from './execute-final-review'
|
|
7
6
|
|
|
8
7
|
import { checkIntake } from '../intake/intake-checks'
|
|
9
8
|
import type { TicketSnapshot } from '../intake/intake-schemas'
|
|
@@ -15,7 +14,11 @@ import {
|
|
|
15
14
|
postCommentOnce,
|
|
16
15
|
type CommentStep,
|
|
17
16
|
} from '../tracker/post-comment-once'
|
|
18
|
-
import
|
|
17
|
+
import {
|
|
18
|
+
HUMAN_LABEL,
|
|
19
|
+
type Tracker,
|
|
20
|
+
type TrackerIssue,
|
|
21
|
+
} from '../tracker/tracker'
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* How long the engine waits between reads of the spec issue while stuck
|
|
@@ -40,8 +43,8 @@ export const ticketChanged = ({
|
|
|
40
43
|
* marking the run stuck on its budget, telling the spec issue a ticket, the
|
|
41
44
|
* run, or the final review is stuck, waiting for
|
|
42
45
|
* and reading its comments, taking or sending back a reply, skipping a
|
|
43
|
-
* ticket (with a comment on it), and
|
|
44
|
-
*
|
|
46
|
+
* ticket (with a comment on it), and retrying the stuck final review
|
|
47
|
+
* (shipping it is `shipFinalReviewIn`'s). Its comments are posted once
|
|
45
48
|
* (`postCommentOnce`), even when a crash cut off the step after posting.
|
|
46
49
|
*/
|
|
47
50
|
export const executeStuckAction = async ({
|
|
@@ -52,8 +55,12 @@ export const executeStuckAction = async ({
|
|
|
52
55
|
reply_poll_ms,
|
|
53
56
|
step,
|
|
54
57
|
}: {
|
|
58
|
+
/** Shipping is `shipFinalReviewIn`'s: it commits edits left over. */
|
|
55
59
|
action:
|
|
56
|
-
| Exclude<
|
|
60
|
+
| Exclude<
|
|
61
|
+
StuckAction,
|
|
62
|
+
{ type: 'undo_join' | 'retry_ticket' | 'ship_final_review' }
|
|
63
|
+
>
|
|
57
64
|
| RunBudgetAction
|
|
58
65
|
journal: Journal
|
|
59
66
|
tracker: Tracker
|
|
@@ -115,6 +122,9 @@ export const executeStuckAction = async ({
|
|
|
115
122
|
ticket: action.ticket,
|
|
116
123
|
comment_id: action.comment_id,
|
|
117
124
|
author: comment?.author ?? '',
|
|
125
|
+
...(action.top_up === undefined
|
|
126
|
+
? {}
|
|
127
|
+
: { top_up: action.top_up }),
|
|
118
128
|
},
|
|
119
129
|
})
|
|
120
130
|
return
|
|
@@ -158,11 +168,6 @@ export const executeStuckAction = async ({
|
|
|
158
168
|
})
|
|
159
169
|
return
|
|
160
170
|
}
|
|
161
|
-
case 'ship_final_review': {
|
|
162
|
-
const shipped = shipFinalReview({ journal })
|
|
163
|
-
if (!shipped.ok) throw new Error(shipped.reason)
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
171
|
case 'retry_final_review':
|
|
167
172
|
journal.append({
|
|
168
173
|
kind: 'final_review_retried',
|
|
@@ -303,6 +308,16 @@ export const retryTicket = async ({
|
|
|
303
308
|
(ticket) => ticket.number === number
|
|
304
309
|
)
|
|
305
310
|
: undefined
|
|
311
|
+
const leftOut = (
|
|
312
|
+
checked.outcome === 'passed'
|
|
313
|
+
? checked.snapshot.left_out
|
|
314
|
+
: checked.left_out
|
|
315
|
+
).some((ticket) => ticket.number === number)
|
|
316
|
+
if (fresh === undefined && leftOut) {
|
|
317
|
+
return refuse([
|
|
318
|
+
`The ticket is now for a person (\`${HUMAN_LABEL}\`), or waits on one, so a run can't build it.`,
|
|
319
|
+
])
|
|
320
|
+
}
|
|
306
321
|
if (fresh === undefined) return refuse(['The ticket is no longer open.'])
|
|
307
322
|
const sha = await resetToRunBranch()
|
|
308
323
|
journal.append({
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carries out the run's waits: a plan limit wait or a billing stop, and a
|
|
3
|
+
* usage-line wait.
|
|
4
|
+
*/
|
|
5
|
+
import type { PlanAction } from './decide-plan'
|
|
6
|
+
import type { UsageLineAction } from './decide-usage-line'
|
|
7
|
+
|
|
8
|
+
import type { Journal } from '../journal/journal'
|
|
9
|
+
import {
|
|
10
|
+
LIMIT_WAIT_NAP_MS,
|
|
11
|
+
limitWaitComment,
|
|
12
|
+
waitUntil,
|
|
13
|
+
type EngineClock,
|
|
14
|
+
} from '../limits/limit-wait'
|
|
15
|
+
import {
|
|
16
|
+
LINE_OF,
|
|
17
|
+
usageLineEndedComment,
|
|
18
|
+
usageLineWaitComment,
|
|
19
|
+
type UsageLines,
|
|
20
|
+
} from '../limits/usage-line'
|
|
21
|
+
import { postCommentOnce, type CommentStep } from '../tracker/post-comment-once'
|
|
22
|
+
import type { Tracker } from '../tracker/tracker'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Carries out a limit wait or a billing stop. A limit wait's start tells the
|
|
26
|
+
* spec issue (unless it already heard of this reset) before it is journaled;
|
|
27
|
+
* the wait itself sleeps by `clock` until its end, so a restarted engine
|
|
28
|
+
* waits out only what is left.
|
|
29
|
+
*/
|
|
30
|
+
export const executePlanAction = async ({
|
|
31
|
+
action,
|
|
32
|
+
journal,
|
|
33
|
+
tracker,
|
|
34
|
+
clock,
|
|
35
|
+
step,
|
|
36
|
+
}: {
|
|
37
|
+
action: Exclude<PlanAction, { type: 'done' }>
|
|
38
|
+
journal: Journal
|
|
39
|
+
tracker: Tracker
|
|
40
|
+
clock: EngineClock
|
|
41
|
+
step: CommentStep
|
|
42
|
+
}): Promise<void> => {
|
|
43
|
+
switch (action.type) {
|
|
44
|
+
case 'start_limit_wait': {
|
|
45
|
+
const { rate_limit_type, resets_at, until, ticket, role } = action
|
|
46
|
+
if (action.announce) {
|
|
47
|
+
await postCommentOnce({
|
|
48
|
+
tracker,
|
|
49
|
+
number: action.spec_number,
|
|
50
|
+
body: limitWaitComment({
|
|
51
|
+
rate_limit_type,
|
|
52
|
+
resets_at,
|
|
53
|
+
until,
|
|
54
|
+
}),
|
|
55
|
+
step,
|
|
56
|
+
n: 0,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
journal.append({
|
|
60
|
+
kind: 'limit_wait_started',
|
|
61
|
+
ticket: null,
|
|
62
|
+
role: null,
|
|
63
|
+
content: {
|
|
64
|
+
resets_at,
|
|
65
|
+
until,
|
|
66
|
+
rate_limit_type,
|
|
67
|
+
hit_ticket: ticket,
|
|
68
|
+
hit_role: role,
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
case 'wait_for_limit':
|
|
74
|
+
await waitUntil({ clock, until: action.until })
|
|
75
|
+
journal.append({
|
|
76
|
+
kind: 'limit_wait_ended',
|
|
77
|
+
ticket: null,
|
|
78
|
+
role: null,
|
|
79
|
+
content: { until: action.until },
|
|
80
|
+
})
|
|
81
|
+
return
|
|
82
|
+
case 'stop_for_billing':
|
|
83
|
+
journal.append({
|
|
84
|
+
kind: 'run_stopped',
|
|
85
|
+
ticket: null,
|
|
86
|
+
role: null,
|
|
87
|
+
content: { reason: action.reason, role: null, billing: true },
|
|
88
|
+
})
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Carries out a usage-line wait. Its start tells the spec issue, then is
|
|
95
|
+
* journaled. The wait sleeps by `clock` in naps until its end, re-reading
|
|
96
|
+
* the lines before each nap: a line raised above the reading ends it at
|
|
97
|
+
* once. Its end tells the spec issue why the run carries on, then is
|
|
98
|
+
* journaled.
|
|
99
|
+
*/
|
|
100
|
+
export const executeUsageLineAction = async ({
|
|
101
|
+
action,
|
|
102
|
+
journal,
|
|
103
|
+
tracker,
|
|
104
|
+
clock,
|
|
105
|
+
read_usage_lines,
|
|
106
|
+
step,
|
|
107
|
+
}: {
|
|
108
|
+
action: UsageLineAction
|
|
109
|
+
journal: Journal
|
|
110
|
+
tracker: Tracker
|
|
111
|
+
clock: EngineClock
|
|
112
|
+
read_usage_lines: () => Promise<UsageLines>
|
|
113
|
+
step: CommentStep
|
|
114
|
+
}): Promise<void> => {
|
|
115
|
+
const { spec_number, window, line, percent, until } = action
|
|
116
|
+
if (action.type === 'start_usage_line_wait') {
|
|
117
|
+
const { resets_at } = action
|
|
118
|
+
await postCommentOnce({
|
|
119
|
+
tracker,
|
|
120
|
+
number: spec_number,
|
|
121
|
+
body: usageLineWaitComment({ window, line, percent, resets_at }),
|
|
122
|
+
step,
|
|
123
|
+
n: 0,
|
|
124
|
+
})
|
|
125
|
+
journal.append({
|
|
126
|
+
kind: 'usage_line_wait_started',
|
|
127
|
+
ticket: null,
|
|
128
|
+
role: null,
|
|
129
|
+
content: { window, line, percent, resets_at, until },
|
|
130
|
+
})
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
const end = Date.parse(until)
|
|
134
|
+
let reason: 'reset' | 'line_raised' = 'reset'
|
|
135
|
+
for (;;) {
|
|
136
|
+
const lines = await read_usage_lines()
|
|
137
|
+
if (lines[LINE_OF[window]] > percent) {
|
|
138
|
+
reason = 'line_raised'
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
const left = end - clock.now()
|
|
142
|
+
if (left <= 0) break
|
|
143
|
+
await clock.sleep(Math.min(left, LIMIT_WAIT_NAP_MS))
|
|
144
|
+
}
|
|
145
|
+
await postCommentOnce({
|
|
146
|
+
tracker,
|
|
147
|
+
number: spec_number,
|
|
148
|
+
body: usageLineEndedComment({ window, percent, reason }),
|
|
149
|
+
step,
|
|
150
|
+
n: 0,
|
|
151
|
+
})
|
|
152
|
+
journal.append({
|
|
153
|
+
kind: 'usage_line_wait_ended',
|
|
154
|
+
ticket: null,
|
|
155
|
+
role: null,
|
|
156
|
+
content: { until, reason },
|
|
157
|
+
})
|
|
158
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carries out one action with the decision model asked around it in shadow
|
|
3
|
+
* mode.
|
|
4
|
+
*/
|
|
5
|
+
import type { EngineAction } from './decide'
|
|
6
|
+
import { isFinalReviewAction } from './decide-final-review'
|
|
7
|
+
import { executeAction, type StepTry } from './execute-action'
|
|
8
|
+
import type { BuildDeps } from './execute-build'
|
|
9
|
+
import type { GoingRunsReader } from './execute-repo-wait'
|
|
10
|
+
import { ticketOf } from './schedule-rules'
|
|
11
|
+
|
|
12
|
+
import { jevAsksAfter, jevAsksBefore } from '../jev/jev-jobs'
|
|
13
|
+
import type { ShadowAsker } from '../jev/jev-shadow'
|
|
14
|
+
import { asksModel } from '../jev/model-pick'
|
|
15
|
+
import type { Journal } from '../journal/journal'
|
|
16
|
+
import type { JournalRecord } from '../journal/journal-record'
|
|
17
|
+
import { replayRun } from '../journal/replay'
|
|
18
|
+
import type { EngineClock } from '../limits/limit-wait'
|
|
19
|
+
import type { UsageLines } from '../limits/usage-line'
|
|
20
|
+
import type { MemoryDeps } from '../memory/memory-client'
|
|
21
|
+
import type { Tracker } from '../tracker/tracker'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Carries out one action with the decision model in shadow mode: asks it
|
|
25
|
+
* before the action (ticket order, model, skills), then after it about the
|
|
26
|
+
* records it appended (failure kinds, finding severities). Only the action's
|
|
27
|
+
* own ticket's new records count, since other tickets append at the same
|
|
28
|
+
* time. The answers change nothing, but for the model pick of a role whose
|
|
29
|
+
* floor is below Opus, which the launch step reads (#603). A launch whose
|
|
30
|
+
* model is set without the decision model (after a failed try on a cheaper
|
|
31
|
+
* model, or a redo) gets no model ask (#604).
|
|
32
|
+
*/
|
|
33
|
+
export const executeWithJev = async ({
|
|
34
|
+
ask,
|
|
35
|
+
action,
|
|
36
|
+
journal,
|
|
37
|
+
tracker,
|
|
38
|
+
build,
|
|
39
|
+
clock,
|
|
40
|
+
reply_poll_ms,
|
|
41
|
+
memory,
|
|
42
|
+
step,
|
|
43
|
+
read_usage_lines,
|
|
44
|
+
going_runs,
|
|
45
|
+
}: {
|
|
46
|
+
ask: ShadowAsker
|
|
47
|
+
action: EngineAction
|
|
48
|
+
journal: Journal
|
|
49
|
+
tracker: Tracker
|
|
50
|
+
build?: BuildDeps
|
|
51
|
+
clock?: EngineClock
|
|
52
|
+
reply_poll_ms?: number
|
|
53
|
+
memory?: MemoryDeps
|
|
54
|
+
step?: StepTry
|
|
55
|
+
read_usage_lines?: () => Promise<UsageLines>
|
|
56
|
+
going_runs?: GoingRunsReader
|
|
57
|
+
}): Promise<void> => {
|
|
58
|
+
const before = journal.read()
|
|
59
|
+
const state = replayRun({ records: before })
|
|
60
|
+
await ask(
|
|
61
|
+
jevAsksBefore({ action, state }).filter(
|
|
62
|
+
// No model ask once the launch's model is set without one (#604).
|
|
63
|
+
({ job, ticket, role }) =>
|
|
64
|
+
job !== 'agent_model' ||
|
|
65
|
+
role === null ||
|
|
66
|
+
asksModel({ records: before, state, ticket, role, step })
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
const lastSeq = journal.read().at(-1)?.seq ?? 0
|
|
70
|
+
await executeAction({
|
|
71
|
+
action,
|
|
72
|
+
journal,
|
|
73
|
+
tracker,
|
|
74
|
+
build,
|
|
75
|
+
clock,
|
|
76
|
+
reply_poll_ms,
|
|
77
|
+
memory,
|
|
78
|
+
step,
|
|
79
|
+
read_usage_lines,
|
|
80
|
+
going_runs,
|
|
81
|
+
})
|
|
82
|
+
const records = journal.read()
|
|
83
|
+
await ask(
|
|
84
|
+
jevAsksAfter({
|
|
85
|
+
records: records.filter(
|
|
86
|
+
(record) =>
|
|
87
|
+
record.seq > lastSeq && ownsRecord({ action, record })
|
|
88
|
+
),
|
|
89
|
+
state: replayRun({ records }),
|
|
90
|
+
})
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Whether a record appended while `action` ran is that action's own, since
|
|
96
|
+
* other tickets (or other lenses) append at the same time: a ticket's
|
|
97
|
+
* records for a ticket's step, a lens's for its lens, and the final
|
|
98
|
+
* review's other ticket-less records for its other steps.
|
|
99
|
+
*/
|
|
100
|
+
const ownsRecord = ({
|
|
101
|
+
action,
|
|
102
|
+
record,
|
|
103
|
+
}: {
|
|
104
|
+
action: EngineAction
|
|
105
|
+
record: JournalRecord
|
|
106
|
+
}): boolean => {
|
|
107
|
+
if (action.type === 'launch_lens') {
|
|
108
|
+
return (
|
|
109
|
+
record.ticket === null &&
|
|
110
|
+
(record.role === null || record.role === action.role)
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
if (isFinalReviewAction(action)) {
|
|
114
|
+
return record.ticket === null && !record.role?.endsWith('-lens')
|
|
115
|
+
}
|
|
116
|
+
const ticket = ticketOf(action)
|
|
117
|
+
return ticket === null || record.ticket === ticket
|
|
118
|
+
}
|