@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,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A run's first records: `run_started`, and on a resume `engine_resumed`
|
|
3
|
+
* and what came of taking the repo's config again.
|
|
4
|
+
*/
|
|
5
|
+
import values from 'lodash/values'
|
|
6
|
+
|
|
7
|
+
import { configChanges } from '../config/config-reload'
|
|
8
|
+
import type {
|
|
9
|
+
EngineConfig,
|
|
10
|
+
LoadEngineConfigResult,
|
|
11
|
+
} from '../config/engine-config'
|
|
12
|
+
import { lucaVersion } from '../config/luca-version'
|
|
13
|
+
import { configProblems } from '../intake/intake-checks'
|
|
14
|
+
import type { Journal } from '../journal/journal'
|
|
15
|
+
import type { JournalRecord } from '../journal/journal-record'
|
|
16
|
+
import { replayRun } from '../journal/replay'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Starts a run by writing its first record. The spec and config it names are
|
|
20
|
+
* what every later step of the run works from; the Luca version it names is
|
|
21
|
+
* the one the run started on. A run started `alongside` journals right away
|
|
22
|
+
* that it skips the repo wait (`repo_wait_skipped`, #615).
|
|
23
|
+
*/
|
|
24
|
+
export const startRun = ({
|
|
25
|
+
journal,
|
|
26
|
+
spec_number,
|
|
27
|
+
config,
|
|
28
|
+
base_branch,
|
|
29
|
+
memory,
|
|
30
|
+
repo,
|
|
31
|
+
alongside,
|
|
32
|
+
luca_version = lucaVersion(),
|
|
33
|
+
}: {
|
|
34
|
+
journal: Journal
|
|
35
|
+
spec_number: number
|
|
36
|
+
config: EngineConfig
|
|
37
|
+
/** The branch the run branch starts from. Defaults to `main`. */
|
|
38
|
+
base_branch?: string
|
|
39
|
+
/**
|
|
40
|
+
* Turns memory on (#370), with the project's vault (`null` searches only
|
|
41
|
+
* `default`). Leave it out for a run without memory.
|
|
42
|
+
*/
|
|
43
|
+
memory?: { project_vault: string | null }
|
|
44
|
+
/** The repo the run is on, so a resume can find it. Defaults to `null`. */
|
|
45
|
+
repo?: string | null
|
|
46
|
+
/** Never wait for another run on the repo (`luca-run --alongside`). */
|
|
47
|
+
alongside?: boolean
|
|
48
|
+
/** Luca's version. Defaults to the engine's own. */
|
|
49
|
+
luca_version?: string
|
|
50
|
+
}): JournalRecord => {
|
|
51
|
+
const started = journal.append({
|
|
52
|
+
kind: 'run_started',
|
|
53
|
+
ticket: null,
|
|
54
|
+
role: null,
|
|
55
|
+
content: {
|
|
56
|
+
spec_number,
|
|
57
|
+
config,
|
|
58
|
+
base_branch,
|
|
59
|
+
memory: memory ?? null,
|
|
60
|
+
repo,
|
|
61
|
+
luca_version,
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
if (alongside === true) {
|
|
65
|
+
journal.append({
|
|
66
|
+
kind: 'repo_wait_skipped',
|
|
67
|
+
ticket: null,
|
|
68
|
+
role: null,
|
|
69
|
+
content: {},
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
return started
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Records that the engine started again on a run's journal, on Luca's
|
|
77
|
+
* version `luca_version`, before its first step.
|
|
78
|
+
*/
|
|
79
|
+
export const recordResume = ({
|
|
80
|
+
journal,
|
|
81
|
+
luca_version = lucaVersion(),
|
|
82
|
+
}: {
|
|
83
|
+
journal: Journal
|
|
84
|
+
/** Luca's version. Defaults to the engine's own. */
|
|
85
|
+
luca_version?: string
|
|
86
|
+
}): JournalRecord =>
|
|
87
|
+
journal.append({
|
|
88
|
+
kind: 'engine_resumed',
|
|
89
|
+
ticket: null,
|
|
90
|
+
role: null,
|
|
91
|
+
content: { luca_version },
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* On a resume, takes the repo's config again (#516), just read as
|
|
96
|
+
* `loaded`, and journals what came of it, right after `recordResume`:
|
|
97
|
+
*
|
|
98
|
+
* - a `config_reloaded` with the build fields (`RELOADED_FIELDS`) that
|
|
99
|
+
* changed, old to new, which replay puts on the run's config;
|
|
100
|
+
* - nothing, when none changed (frozen fields never count, see
|
|
101
|
+
* `FROZEN_FIELDS`);
|
|
102
|
+
* - a `config_reload_refused` with the reason, when the file is missing,
|
|
103
|
+
* not JSON, not a valid config, or one intake would refuse
|
|
104
|
+
* (`configProblems`, on the run's tickets). The run keeps its config.
|
|
105
|
+
*
|
|
106
|
+
* Never throws for a bad file: the run always goes on. Returns the record
|
|
107
|
+
* it appended, or `null`. A journal with no config yet (not a run) gets
|
|
108
|
+
* nothing; the decision step says what is wrong with it.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* recordResume({ journal })
|
|
112
|
+
* const reload = recordConfigReload({ journal, loaded: await loadEngineConfig({ repo_root }) })
|
|
113
|
+
*/
|
|
114
|
+
export const recordConfigReload = ({
|
|
115
|
+
journal,
|
|
116
|
+
loaded,
|
|
117
|
+
}: {
|
|
118
|
+
journal: Journal
|
|
119
|
+
loaded: LoadEngineConfigResult
|
|
120
|
+
}): JournalRecord | null => {
|
|
121
|
+
const state = replayRun({ records: journal.read() })
|
|
122
|
+
if (state.config === null) return null
|
|
123
|
+
const refuse = (reason: string): JournalRecord =>
|
|
124
|
+
journal.append({
|
|
125
|
+
kind: 'config_reload_refused',
|
|
126
|
+
ticket: null,
|
|
127
|
+
role: null,
|
|
128
|
+
content: { reason },
|
|
129
|
+
})
|
|
130
|
+
if (!loaded.ok) return refuse(loaded.error)
|
|
131
|
+
const problems = configProblems({
|
|
132
|
+
config: loaded.config,
|
|
133
|
+
tickets: values(state.snapshot?.tickets ?? {}),
|
|
134
|
+
})
|
|
135
|
+
if (problems.length > 0) return refuse(problems.join('\n'))
|
|
136
|
+
const changes = configChanges({
|
|
137
|
+
current: state.config,
|
|
138
|
+
next: loaded.config,
|
|
139
|
+
})
|
|
140
|
+
return changes.length === 0
|
|
141
|
+
? null
|
|
142
|
+
: journal.append({
|
|
143
|
+
kind: 'config_reloaded',
|
|
144
|
+
ticket: null,
|
|
145
|
+
role: null,
|
|
146
|
+
content: { changes },
|
|
147
|
+
})
|
|
148
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The comments on the spec issue when the whole run is stuck: on its run
|
|
3
|
+
* budget, or on a pull request that didn't open.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** A token count with thousands separators, such as `50,000`. */
|
|
7
|
+
const tokensText = (tokens: number): string =>
|
|
8
|
+
String(tokens).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Why the run is stuck on its budget, in one line.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* runBudgetDetail({ tokens: 50_100, budget: 50_000 })
|
|
15
|
+
* // 'The run used 50,100 tokens of its run budget of 50,000.'
|
|
16
|
+
*/
|
|
17
|
+
export const runBudgetDetail = ({
|
|
18
|
+
tokens,
|
|
19
|
+
budget,
|
|
20
|
+
}: {
|
|
21
|
+
tokens: number
|
|
22
|
+
budget: number
|
|
23
|
+
}): string =>
|
|
24
|
+
`The run used ${tokensText(tokens)} tokens of its run budget of ${tokensText(budget)}.`
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The comment on the spec issue when the whole run is stuck on its run
|
|
28
|
+
* budget: why, how big one budget is, and the replies `retry` and `stop`
|
|
29
|
+
* (never `skip` or `ship`).
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* runStuckComment({ detail, budget: 9_000_000 })
|
|
33
|
+
* // '**The run is stuck: it used up its run budget**\n\nWhy: ...'
|
|
34
|
+
*/
|
|
35
|
+
export const runStuckComment = ({
|
|
36
|
+
detail,
|
|
37
|
+
budget,
|
|
38
|
+
}: {
|
|
39
|
+
detail: string
|
|
40
|
+
/** One full run budget. */
|
|
41
|
+
budget: number
|
|
42
|
+
}): string =>
|
|
43
|
+
[
|
|
44
|
+
'**The run is stuck: it used up its run budget**',
|
|
45
|
+
[
|
|
46
|
+
`Why: ${detail}`,
|
|
47
|
+
`One run budget is ${tokensText(budget)} tokens (input, output, and cache writes; cache reads don't count). A repo changes it with \`run_budget_tokens\` in \`.luca/config.json\`.`,
|
|
48
|
+
'Nothing new starts until you reply. Other stuck work waits for its reply until the run carries on.',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
[
|
|
51
|
+
'Reply with one word on this issue:',
|
|
52
|
+
`- \`retry\`: add half a run budget (${tokensText(Math.floor(budget / 2))} tokens) and carry on where it stopped. \`retry +500k\` or \`retry +3M\` adds the amount you name instead.`,
|
|
53
|
+
'- `stop`: end the run without a PR. The branch is kept.',
|
|
54
|
+
'',
|
|
55
|
+
'Only the spec owner counts.',
|
|
56
|
+
].join('\n'),
|
|
57
|
+
].join('\n\n')
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The comment on the spec issue when the run is stuck because its pull
|
|
61
|
+
* request didn't open (#508): `gh`'s error, where the PR's full text is
|
|
62
|
+
* saved, and the replies `retry` and `stop`.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* pullRequestStuckComment({ detail: 'gh pr create failed (exit code 1): ...' })
|
|
66
|
+
* // "**The run is stuck: its pull request didn't open**\n\nWhy: ..."
|
|
67
|
+
*/
|
|
68
|
+
export const pullRequestStuckComment = ({
|
|
69
|
+
detail,
|
|
70
|
+
}: {
|
|
71
|
+
detail: string
|
|
72
|
+
}): string =>
|
|
73
|
+
[
|
|
74
|
+
"**The run is stuck: its pull request didn't open**",
|
|
75
|
+
[
|
|
76
|
+
`Why: ${detail}`,
|
|
77
|
+
`The run branch is pushed, and the PR's full text is saved in the run folder as \`pull-request.md\`.`,
|
|
78
|
+
].join('\n'),
|
|
79
|
+
[
|
|
80
|
+
'Reply with one word on this issue:',
|
|
81
|
+
'- `retry`: try to open the PR again. A PR already open from the run branch, such as one you opened by hand, is reused, never opened twice.',
|
|
82
|
+
'- `stop`: end the run without a PR. The branch is kept.',
|
|
83
|
+
'',
|
|
84
|
+
'Only the spec owner counts.',
|
|
85
|
+
].join('\n'),
|
|
86
|
+
].join('\n\n')
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the scheduler knows of an action: its key, its step's name, its
|
|
3
|
+
* ticket and role, and which actions wait, run slow, or use the run branch.
|
|
4
|
+
*/
|
|
5
|
+
import has from 'lodash/has'
|
|
6
|
+
import partition from 'lodash/partition'
|
|
7
|
+
|
|
8
|
+
import type { EngineAction } from './decide'
|
|
9
|
+
import { isFinalReviewAction } from './decide-final-review'
|
|
10
|
+
|
|
11
|
+
import type { JournalRecord } from '../journal/journal-record'
|
|
12
|
+
import { crashesAfter, type CrashCounts } from '../journal/step-records'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The ticket an action works on, or `null` for a run-level action. The
|
|
16
|
+
* plan's actions are the whole run's, whichever ticket hit the limit; a
|
|
17
|
+
* ticket's usage record is that ticket's. Reading and taking replies is
|
|
18
|
+
* the run's too (see `keyOf`).
|
|
19
|
+
*/
|
|
20
|
+
export const ticketOf = (action: EngineAction): number | null => {
|
|
21
|
+
switch (action.type) {
|
|
22
|
+
case 'start_limit_wait':
|
|
23
|
+
case 'wait_for_limit':
|
|
24
|
+
case 'stop_for_billing':
|
|
25
|
+
case 'wait_for_reply':
|
|
26
|
+
case 'take_reply':
|
|
27
|
+
case 'ignore_reply':
|
|
28
|
+
return null
|
|
29
|
+
case 'record_usage':
|
|
30
|
+
return action.usage.ticket
|
|
31
|
+
default:
|
|
32
|
+
return 'ticket' in action && typeof action.ticket === 'number'
|
|
33
|
+
? action.ticket
|
|
34
|
+
: null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Replies are read and taken beside the other steps, one at a time, under
|
|
40
|
+
* their own key: waiting for a reply never holds a ticket up.
|
|
41
|
+
*/
|
|
42
|
+
const REPLY_ACTIONS: ReadonlySet<EngineAction['type']> = new Set([
|
|
43
|
+
'wait_for_reply',
|
|
44
|
+
'take_reply',
|
|
45
|
+
'ignore_reply',
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The scheduler's key for an action: at most one action per key runs at a
|
|
50
|
+
* time. Reading and taking replies has its own (`replies`). Each lens has
|
|
51
|
+
* its own (`lens:<lens>`), so the five lenses review at once; the final review's other steps share `final`; a ticket's steps its
|
|
52
|
+
* number; and the run's own steps `run`, which only ever run alone.
|
|
53
|
+
*/
|
|
54
|
+
export const keyOf = (action: EngineAction): string => {
|
|
55
|
+
if (REPLY_ACTIONS.has(action.type)) return 'replies'
|
|
56
|
+
// A search waits under the key of the step it comes before: its
|
|
57
|
+
// ticket's, the final review's, or (the run's start) the run's.
|
|
58
|
+
if (action.type === 'recall_memories') {
|
|
59
|
+
if (action.ticket !== null) return String(action.ticket)
|
|
60
|
+
return action.point === 'run_start' ? 'run' : 'final'
|
|
61
|
+
}
|
|
62
|
+
if (action.type === 'launch_lens') return `lens:${action.lens}`
|
|
63
|
+
if (isFinalReviewAction(action)) return 'final'
|
|
64
|
+
const ticket = ticketOf(action)
|
|
65
|
+
return ticket === null ? 'run' : String(ticket)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Actions that are an agent's turn: their step names the role. */
|
|
69
|
+
const AGENT_ACTIONS: ReadonlySet<EngineAction['type']> = new Set([
|
|
70
|
+
'launch_agent',
|
|
71
|
+
'follow_up_agent',
|
|
72
|
+
'launch_lens',
|
|
73
|
+
'launch_final_fixer',
|
|
74
|
+
'follow_up_final_fixer',
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
/** The agent role of an agent's turn (the learner's is `learner`), else `null`. */
|
|
78
|
+
export const agentRoleOf = (action: EngineAction): string | null => {
|
|
79
|
+
if (action.type === 'launch_learner') return 'learner'
|
|
80
|
+
return AGENT_ACTIONS.has(action.type) &&
|
|
81
|
+
'role' in action &&
|
|
82
|
+
action.role !== null
|
|
83
|
+
? action.role
|
|
84
|
+
: null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A step's name in the journal: its action type, plus `:<role>` for an
|
|
89
|
+
* agent's turn, such as `follow_up_agent:implementer`.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* stepOf({ type: 'run_gates', ticket: 11, target: 'ticket' }) // 'run_gates'
|
|
93
|
+
*/
|
|
94
|
+
export const stepOf = (action: EngineAction): string => {
|
|
95
|
+
const role = agentRoleOf(action)
|
|
96
|
+
return role === null ? action.type : `${action.type}:${role}`
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Waits get no step records: a crash during one is no step's fault, and a
|
|
101
|
+
* restarted engine just waits again (a limit wait until the same time).
|
|
102
|
+
*/
|
|
103
|
+
export const WAIT_ACTIONS: ReadonlySet<EngineAction['type']> = new Set([
|
|
104
|
+
'wait_for_reply',
|
|
105
|
+
'wait_for_limit',
|
|
106
|
+
'wait_for_usage_line',
|
|
107
|
+
'wait_for_repo',
|
|
108
|
+
])
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Steps that can run for minutes: installs, test runs, and agents' turns.
|
|
112
|
+
* The board hears of them as they start, so a run never looks frozen.
|
|
113
|
+
*/
|
|
114
|
+
export const SLOW_ACTIONS: ReadonlySet<EngineAction['type']> = new Set([
|
|
115
|
+
...AGENT_ACTIONS,
|
|
116
|
+
'launch_learner',
|
|
117
|
+
'install_dependencies',
|
|
118
|
+
'run_baseline_tests',
|
|
119
|
+
'run_red_check',
|
|
120
|
+
'check_already_done',
|
|
121
|
+
'run_gates',
|
|
122
|
+
'run_final_gates',
|
|
123
|
+
'run_final_slow_tests',
|
|
124
|
+
])
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Actions that read or move the run branch. At most one runs at a time, so
|
|
128
|
+
* joins, their gates, pushes, and new worktrees see one run branch. The
|
|
129
|
+
* final review's fixers, gates, slow tests (#563), commit, and push work in
|
|
130
|
+
* the run branch's worktree; its lenses only read it. Nothing writes there
|
|
131
|
+
* while a lens runs, though: its after-turn guard judges that worktree, so
|
|
132
|
+
* the slow tests wait for round 1's lenses (see `decideFinalReview`).
|
|
133
|
+
*/
|
|
134
|
+
export const usesRunBranch = (action: EngineAction): boolean => {
|
|
135
|
+
switch (action.type) {
|
|
136
|
+
case 'create_ticket_worktree':
|
|
137
|
+
case 'join_run_branch':
|
|
138
|
+
case 'push_run_branch':
|
|
139
|
+
case 'rebase_ticket':
|
|
140
|
+
case 'start_final_review':
|
|
141
|
+
case 'launch_final_fixer':
|
|
142
|
+
case 'follow_up_final_fixer':
|
|
143
|
+
case 'run_final_gates':
|
|
144
|
+
case 'run_final_slow_tests':
|
|
145
|
+
case 'commit_final_fix':
|
|
146
|
+
case 'push_final_fixes':
|
|
147
|
+
case 'write_changeset':
|
|
148
|
+
case 'ship_final_review':
|
|
149
|
+
case 'undo_join':
|
|
150
|
+
case 'retry_ticket':
|
|
151
|
+
return true
|
|
152
|
+
case 'run_gates':
|
|
153
|
+
case 'install_dependencies':
|
|
154
|
+
return action.target === 'run_branch'
|
|
155
|
+
// It puts back a run branch a join crashes cut off too often left.
|
|
156
|
+
case 'mark_stuck':
|
|
157
|
+
return action.reason === 'crashed'
|
|
158
|
+
default:
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The crash counts per key, from the journal's records. */
|
|
164
|
+
export const crashesIn = (records: JournalRecord[]): CrashCounts =>
|
|
165
|
+
records.reduce<CrashCounts>(
|
|
166
|
+
(crashes, record) => crashesAfter({ crashes, record }),
|
|
167
|
+
{}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The actions on keys a crash cut a step off first, in their order, then
|
|
172
|
+
* the rest: a step a crash cut off (or the stop for it) starts before any
|
|
173
|
+
* other, so a half-done change to the run branch, such as a join, is put
|
|
174
|
+
* right before another step reads the run branch.
|
|
175
|
+
*/
|
|
176
|
+
export const redosFirst = ({
|
|
177
|
+
actions,
|
|
178
|
+
crashes,
|
|
179
|
+
}: {
|
|
180
|
+
actions: EngineAction[]
|
|
181
|
+
crashes: CrashCounts
|
|
182
|
+
}): EngineAction[] => {
|
|
183
|
+
const [redos, rest] = partition(actions, (action) =>
|
|
184
|
+
has(crashes, keyOf(action))
|
|
185
|
+
)
|
|
186
|
+
return [...redos, ...rest]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The session a follow-up goes to, if the action is one. */
|
|
190
|
+
export const followUpSessionOf = (action: EngineAction): string[] =>
|
|
191
|
+
action.type === 'follow_up_agent' || action.type === 'follow_up_final_fixer'
|
|
192
|
+
? [action.session_id]
|
|
193
|
+
: []
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stuck details for a ticket that needs a change only the owner may make, or
|
|
3
|
+
* whose test was sent back as bad.
|
|
4
|
+
*/
|
|
5
|
+
import upperFirst from 'lodash/upperFirst'
|
|
6
|
+
|
|
7
|
+
import type { AgentRole, BadTest, BadTestSource } from '../agents/role-results'
|
|
8
|
+
import { clipOutput } from '../shell/run-command'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The detail of a ticket or the final review stuck on a change only the
|
|
12
|
+
* owner may make (#617): the file, why, and what to do. It says "test setup
|
|
13
|
+
* file" only when the file is one of the config's.
|
|
14
|
+
*/
|
|
15
|
+
export const setupChangeDetail = ({
|
|
16
|
+
role,
|
|
17
|
+
file,
|
|
18
|
+
reason,
|
|
19
|
+
setup_files,
|
|
20
|
+
}: {
|
|
21
|
+
role: AgentRole
|
|
22
|
+
file: string
|
|
23
|
+
reason: string
|
|
24
|
+
/** The config's test setup files. */
|
|
25
|
+
setup_files: string[]
|
|
26
|
+
}): string =>
|
|
27
|
+
`The ${role} needs a change only you may make, to \`${file || 'unnamed'}\`: ${reason || 'no reason given'}\n` +
|
|
28
|
+
`${setup_files.includes(file) ? 'It is a test setup file, which agents may never change.' : 'No agent may make it.'} Make the change yourself in the worktree, then reply retry.`
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A test sent back as bad, for a stuck detail: its file and name, then the
|
|
32
|
+
* reason (the implementer's, or the engine's finding that it checks nothing,
|
|
33
|
+
* #581), clipped like other outputs (#490). "no reason given" only when
|
|
34
|
+
* there is none.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* badTestDetail({ bad_test: { file: 'src/sum.test.ts', name: 'sum > adds', reason: 'Wrong sum.' } })
|
|
38
|
+
* // 'src/sum.test.ts > sum > adds: Wrong sum.'
|
|
39
|
+
*/
|
|
40
|
+
export const badTestDetail = ({
|
|
41
|
+
bad_test,
|
|
42
|
+
}: {
|
|
43
|
+
bad_test: BadTest | null
|
|
44
|
+
}): string => {
|
|
45
|
+
const reason =
|
|
46
|
+
bad_test === null || bad_test.reason.trim() === ''
|
|
47
|
+
? 'no reason given'
|
|
48
|
+
: clipOutput({ text: bad_test.reason })
|
|
49
|
+
const where =
|
|
50
|
+
bad_test === null
|
|
51
|
+
? ''
|
|
52
|
+
: [bad_test.file, bad_test.name].filter(Boolean).join(' > ')
|
|
53
|
+
return where === '' ? reason : `${where}: ${reason}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** What each source did when it sent a test back as bad (#581). */
|
|
57
|
+
const BOUNCE_CLAUSES: Record<BadTestSource, string> = {
|
|
58
|
+
implementer: 'the implementer sent a test back as bad',
|
|
59
|
+
engine: 'the engine found a mapped test that checks nothing',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The start of a stuck detail for a test sent back as bad a second time
|
|
64
|
+
* (#581): who sent the first back, and who the second. A test can be sent
|
|
65
|
+
* back by the implementer, or by the engine when a mapped test passes with no
|
|
66
|
+
* `expect()`; the two share one cap, so the second may have another source
|
|
67
|
+
* than the first.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* badTestTwiceText({ earlier: 'engine', latest: 'implementer' })
|
|
71
|
+
* // 'The engine found a mapped test that checks nothing, and then, after a
|
|
72
|
+
* // fresh test-writer, the implementer sent a test back as bad'
|
|
73
|
+
*/
|
|
74
|
+
export const badTestTwiceText = ({
|
|
75
|
+
earlier,
|
|
76
|
+
latest,
|
|
77
|
+
}: {
|
|
78
|
+
earlier: BadTestSource
|
|
79
|
+
latest: BadTestSource
|
|
80
|
+
}): string => {
|
|
81
|
+
if (earlier === latest) {
|
|
82
|
+
return latest === 'engine'
|
|
83
|
+
? 'A mapped test still checks nothing after a fresh test-writer'
|
|
84
|
+
: 'The implementer sent a test back as bad a second time'
|
|
85
|
+
}
|
|
86
|
+
return upperFirst(
|
|
87
|
+
`${BOUNCE_CLAUSES[earlier]}, and then, after a fresh test-writer, ${BOUNCE_CLAUSES[latest]}`
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting the stuck comments share: a clipped code fence for error text,
|
|
3
|
+
* and counted words.
|
|
4
|
+
*/
|
|
5
|
+
import max from 'lodash/max'
|
|
6
|
+
|
|
7
|
+
/** Longest error text a comment or prompt quotes. */
|
|
8
|
+
const DETAIL_MAX = 3000
|
|
9
|
+
|
|
10
|
+
/** A code fence longer than any run of backticks in `text`. */
|
|
11
|
+
export const fenced = (text: string): string => {
|
|
12
|
+
const longest = max((text.match(/`+/g) ?? []).map(({ length }) => length))
|
|
13
|
+
const fence = '`'.repeat(Math.max(3, (longest ?? 0) + 1))
|
|
14
|
+
const clipped =
|
|
15
|
+
text.length > DETAIL_MAX
|
|
16
|
+
? `${text.slice(0, DETAIL_MAX)}\n... (clipped)`
|
|
17
|
+
: text
|
|
18
|
+
return `${fence}\n${clipped}\n${fence}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const plural = (count: number, word: string): string =>
|
|
22
|
+
`${count} ${word}${count === 1 ? '' : 's'}`
|