@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,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A writer's `environment` answer (#613): when its rerun makes the ticket
|
|
3
|
+
* (or the final review) stuck as `environment_failed`, and the stuck
|
|
4
|
+
* detail, with the agent's evidence and the clipped output. Pure.
|
|
5
|
+
*/
|
|
6
|
+
import { MAX_ENVIRONMENT_RERUNS } from './loop-caps'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
EnvironmentRerun,
|
|
10
|
+
ReplayedGates,
|
|
11
|
+
ReplayedRedCheck,
|
|
12
|
+
} from '../journal/replay'
|
|
13
|
+
import { clipOutput } from '../shell/run-command'
|
|
14
|
+
|
|
15
|
+
/** A red check or a gate run. */
|
|
16
|
+
type CheckRun = ReplayedRedCheck | ReplayedGates
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Longest output kept per failed check in the stuck detail, so the stuck
|
|
20
|
+
* comment shows its start and its end.
|
|
21
|
+
*/
|
|
22
|
+
const OUTPUT_MAX = 2000
|
|
23
|
+
|
|
24
|
+
/** How a check failed: the red check's problems, or the failed gates' names. */
|
|
25
|
+
const failureKey = (run: CheckRun): string =>
|
|
26
|
+
'checks' in run
|
|
27
|
+
? run.checks
|
|
28
|
+
.filter(({ ok }) => !ok)
|
|
29
|
+
.map(({ name }) => name)
|
|
30
|
+
.sort()
|
|
31
|
+
.join('\n')
|
|
32
|
+
: [...run.problems].sort().join('\n')
|
|
33
|
+
|
|
34
|
+
/** The failure, with its clipped output. */
|
|
35
|
+
const failureText = (run: CheckRun): string =>
|
|
36
|
+
'checks' in run
|
|
37
|
+
? run.checks
|
|
38
|
+
.filter(({ ok }) => !ok)
|
|
39
|
+
.map(
|
|
40
|
+
({ name, output }) =>
|
|
41
|
+
`${name} failed:\n${clipOutput({ text: output, max: OUTPUT_MAX })}`
|
|
42
|
+
)
|
|
43
|
+
.join('\n\n')
|
|
44
|
+
: [
|
|
45
|
+
`The red check's problems:\n${run.problems.map((problem) => `- ${problem}`).join('\n')}`,
|
|
46
|
+
`Test output:\n${clipOutput({ text: run.output, max: OUTPUT_MAX })}`,
|
|
47
|
+
].join('\n\n')
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Why an `environment` answer leaves the work stuck, or `null` to go on:
|
|
51
|
+
* its rerun is past the cap of `MAX_ENVIRONMENT_RERUNS`, or `latest`, the
|
|
52
|
+
* rerun, failed the same way (the same problems, or the same failed
|
|
53
|
+
* gates). `latest` is `null` while the rerun is due.
|
|
54
|
+
*/
|
|
55
|
+
export const environmentFailedDetail = ({
|
|
56
|
+
environment,
|
|
57
|
+
reruns,
|
|
58
|
+
latest,
|
|
59
|
+
}: {
|
|
60
|
+
environment: EnvironmentRerun | null
|
|
61
|
+
/** `environment` answers so far, this one included. */
|
|
62
|
+
reruns: number
|
|
63
|
+
latest: CheckRun | null
|
|
64
|
+
}): string | null => {
|
|
65
|
+
if (environment === null) return null
|
|
66
|
+
const answer = `The ${environment.role} answered "environment": ${environment.evidence}`
|
|
67
|
+
if (latest === null) {
|
|
68
|
+
if (reruns <= MAX_ENVIRONMENT_RERUNS) return null
|
|
69
|
+
return [
|
|
70
|
+
answer,
|
|
71
|
+
`That is past the cap of ${MAX_ENVIRONMENT_RERUNS} environment reruns, so the check did not run again. It failed like this:`,
|
|
72
|
+
failureText(environment.failed),
|
|
73
|
+
].join('\n\n')
|
|
74
|
+
}
|
|
75
|
+
if (latest.ok || failureKey(latest) !== failureKey(environment.failed)) {
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
return [
|
|
79
|
+
answer,
|
|
80
|
+
'The engine reran the check, and it failed the same way:',
|
|
81
|
+
failureText(latest),
|
|
82
|
+
].join('\n\n')
|
|
83
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { TicketProgress, ReplayedSnapshot } from '../journal/replay'
|
|
2
|
+
import { REFACTOR_LABEL } from '../tracker/tracker'
|
|
3
|
+
|
|
4
|
+
/** The most test names one ticket's Evidence line lists (#513). */
|
|
5
|
+
export const MAX_EVIDENCE_TESTS = 5
|
|
6
|
+
|
|
7
|
+
/** The longest test name an Evidence line shows; a longer one is clipped. */
|
|
8
|
+
export const MAX_EVIDENCE_NAME = 80
|
|
9
|
+
|
|
10
|
+
const EVIDENCE_INTRO =
|
|
11
|
+
"Each ticket's new tests failed before its code was written (the red check), and pass now (the gates)."
|
|
12
|
+
|
|
13
|
+
const clipName = (name: string): string =>
|
|
14
|
+
name.length > MAX_EVIDENCE_NAME
|
|
15
|
+
? `${name.slice(0, MAX_EVIDENCE_NAME - 1)}…`
|
|
16
|
+
: name
|
|
17
|
+
|
|
18
|
+
/** Up to `MAX_EVIDENCE_TESTS` names, then how many more. */
|
|
19
|
+
const namesText = (names: string[]): string => {
|
|
20
|
+
const shown = names.slice(0, MAX_EVIDENCE_TESTS).map(clipName)
|
|
21
|
+
const more = names.length - shown.length
|
|
22
|
+
return more > 0 ? `${shown.join(', ')}, and ${more} more` : shown.join(', ')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One ticket's Evidence line. */
|
|
26
|
+
const evidenceLine = ({
|
|
27
|
+
number,
|
|
28
|
+
progress,
|
|
29
|
+
refactor,
|
|
30
|
+
}: {
|
|
31
|
+
number: number
|
|
32
|
+
progress: TicketProgress | undefined
|
|
33
|
+
refactor: boolean
|
|
34
|
+
}): string => {
|
|
35
|
+
if ((progress?.already_done ?? null) !== null) {
|
|
36
|
+
return `- #${number}: already done on the base branch, so no new tests.`
|
|
37
|
+
}
|
|
38
|
+
if (refactor) {
|
|
39
|
+
return `- #${number}: a refactor, so no new tests; the old tests still pass.`
|
|
40
|
+
}
|
|
41
|
+
const red_tests = progress?.red_tests ?? []
|
|
42
|
+
if (red_tests.length === 0) return `- #${number}: no red check recorded.`
|
|
43
|
+
const one = red_tests.length === 1
|
|
44
|
+
const count = `${red_tests.length} new ${one ? 'test' : 'tests'} failed first`
|
|
45
|
+
const passed =
|
|
46
|
+
progress?.gates?.ok === true || progress?.join_gates?.ok === true
|
|
47
|
+
const how = passed
|
|
48
|
+
? `, now ${one ? 'passes' : 'pass'}`
|
|
49
|
+
: `; the gates have not passed ${one ? 'it' : 'them'} yet`
|
|
50
|
+
return `- #${number}: ${count}${how}: ${namesText(red_tests.map(({ name }) => name))}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The PR's Evidence section (#513), from the journal alone: for each ticket
|
|
55
|
+
* in the run (not the skipped ones), the new tests its red check proved
|
|
56
|
+
* fail first, and whether the gates pass them now. An already-done ticket
|
|
57
|
+
* and a refactor ticket say why they have no new tests. Each line names at
|
|
58
|
+
* most `MAX_EVIDENCE_TESTS` tests. With no ticket in the run, `''`.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* evidenceSection({ snapshot, tickets })
|
|
62
|
+
* // "## Evidence\n\n...\n\n- #11: 1 new test failed first, now passes: sum adds two numbers"
|
|
63
|
+
*/
|
|
64
|
+
export const evidenceSection = ({
|
|
65
|
+
snapshot,
|
|
66
|
+
tickets,
|
|
67
|
+
}: {
|
|
68
|
+
snapshot: ReplayedSnapshot
|
|
69
|
+
tickets: Record<number, TicketProgress>
|
|
70
|
+
}): string => {
|
|
71
|
+
const lines = snapshot.ticket_order
|
|
72
|
+
.filter((number) => (tickets[number]?.skipped ?? null) === null)
|
|
73
|
+
.map((number) =>
|
|
74
|
+
evidenceLine({
|
|
75
|
+
number,
|
|
76
|
+
progress: tickets[number],
|
|
77
|
+
refactor:
|
|
78
|
+
snapshot.tickets[number]?.labels.includes(REFACTOR_LABEL) ??
|
|
79
|
+
false,
|
|
80
|
+
})
|
|
81
|
+
)
|
|
82
|
+
if (lines.length === 0) return ''
|
|
83
|
+
return `## Evidence\n\n${EVIDENCE_INTRO}\n\n${lines.join('\n')}`
|
|
84
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carries out one action: sends it to the module for its kind of step, or
|
|
3
|
+
* journals it straight away.
|
|
4
|
+
*/
|
|
5
|
+
import { basename, dirname } from 'node:path'
|
|
6
|
+
|
|
7
|
+
import type { EngineAction } from './decide'
|
|
8
|
+
import { isFinalReviewAction } from './decide-final-review'
|
|
9
|
+
import { isMemoryAction } from './decide-memory'
|
|
10
|
+
import { executeAlreadyDoneAction } from './execute-already-done'
|
|
11
|
+
import {
|
|
12
|
+
buildContext,
|
|
13
|
+
executeBuildAction,
|
|
14
|
+
type BuildDeps,
|
|
15
|
+
} from './execute-build'
|
|
16
|
+
import { writeChangeset } from './execute-changeset'
|
|
17
|
+
import {
|
|
18
|
+
executeFinalReviewAction,
|
|
19
|
+
shipFinalReviewIn,
|
|
20
|
+
} from './execute-final-review'
|
|
21
|
+
import { readIntake, refuseIntake } from './execute-intake'
|
|
22
|
+
import { executeMemoryAction } from './execute-memory'
|
|
23
|
+
import { openPullRequest } from './execute-pull-request'
|
|
24
|
+
import {
|
|
25
|
+
executeRepoWaitAction,
|
|
26
|
+
type GoingRunsReader,
|
|
27
|
+
} from './execute-repo-wait'
|
|
28
|
+
import { executeStuckAction } from './execute-stuck'
|
|
29
|
+
import { executePlanAction, executeUsageLineAction } from './execute-waits'
|
|
30
|
+
|
|
31
|
+
import type { Journal } from '../journal/journal'
|
|
32
|
+
import { SYSTEM_CLOCK, type EngineClock } from '../limits/limit-wait'
|
|
33
|
+
import {
|
|
34
|
+
DEFAULT_USAGE_LINES,
|
|
35
|
+
LINE_OF,
|
|
36
|
+
type UsageLines,
|
|
37
|
+
} from '../limits/usage-line'
|
|
38
|
+
import type { MemoryDeps } from '../memory/memory-client'
|
|
39
|
+
import type { CommentStep } from '../tracker/post-comment-once'
|
|
40
|
+
import type { Tracker } from '../tracker/tracker'
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Which try of its step an action is: the seq of the step's first try (its
|
|
44
|
+
* first `step_started`), and whether this try redoes one a crash cut off.
|
|
45
|
+
* A redo adopts the side effects its first try left behind: its comments,
|
|
46
|
+
* its PR, its worktrees and commits.
|
|
47
|
+
*/
|
|
48
|
+
export type StepTry = { first_seq: number; redo: boolean }
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Carries out one action: talks to the tracker, then records what happened in
|
|
52
|
+
* the journal. The only impure half of the engine; `decide` picks the action.
|
|
53
|
+
*
|
|
54
|
+
* Stop actions (`done`, `invalid_journal`) do nothing here.
|
|
55
|
+
*/
|
|
56
|
+
export const executeAction = async ({
|
|
57
|
+
action,
|
|
58
|
+
journal,
|
|
59
|
+
tracker,
|
|
60
|
+
build,
|
|
61
|
+
clock,
|
|
62
|
+
reply_poll_ms,
|
|
63
|
+
memory,
|
|
64
|
+
step,
|
|
65
|
+
read_usage_lines,
|
|
66
|
+
going_runs,
|
|
67
|
+
}: {
|
|
68
|
+
action: EngineAction
|
|
69
|
+
journal: Journal
|
|
70
|
+
tracker: Tracker
|
|
71
|
+
/** Needed for every step after intake. */
|
|
72
|
+
build?: BuildDeps
|
|
73
|
+
/** Limit waits and reply waits sleep by it. Defaults to `SYSTEM_CLOCK`. */
|
|
74
|
+
clock?: EngineClock
|
|
75
|
+
/** How long a reply wait sleeps before it reads the spec issue. */
|
|
76
|
+
reply_poll_ms?: number
|
|
77
|
+
/** The memory client, for a run with memory on (#370). */
|
|
78
|
+
memory?: MemoryDeps
|
|
79
|
+
/**
|
|
80
|
+
* Which try of its step this is, from the scheduler. Left out, it is a
|
|
81
|
+
* first try, numbered after the journal's last record.
|
|
82
|
+
*/
|
|
83
|
+
step?: StepTry
|
|
84
|
+
/**
|
|
85
|
+
* Reads the `luca-board` usage lines, again before each nap of a
|
|
86
|
+
* usage-line wait. Left out, the lines never change during a wait.
|
|
87
|
+
*/
|
|
88
|
+
read_usage_lines?: () => Promise<UsageLines>
|
|
89
|
+
/**
|
|
90
|
+
* Lists the unfinished runs, again before each nap of a repo wait.
|
|
91
|
+
* Left out, no other run is going.
|
|
92
|
+
*/
|
|
93
|
+
going_runs?: GoingRunsReader
|
|
94
|
+
}): Promise<void> => {
|
|
95
|
+
const tried: CommentStep = {
|
|
96
|
+
run_id: basename(dirname(journal.file)),
|
|
97
|
+
...(step ?? {
|
|
98
|
+
first_seq: (journal.read().at(-1)?.seq ?? 0) + 1,
|
|
99
|
+
redo: false,
|
|
100
|
+
}),
|
|
101
|
+
}
|
|
102
|
+
if (isMemoryAction(action)) {
|
|
103
|
+
return executeMemoryAction({
|
|
104
|
+
action,
|
|
105
|
+
journal,
|
|
106
|
+
tracker,
|
|
107
|
+
memory,
|
|
108
|
+
build,
|
|
109
|
+
step: tried,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
switch (action.type) {
|
|
113
|
+
case 'report_stuck':
|
|
114
|
+
case 'wait_for_reply':
|
|
115
|
+
case 'take_reply':
|
|
116
|
+
case 'ignore_reply':
|
|
117
|
+
case 'skip_ticket':
|
|
118
|
+
case 'report_final_review_stuck':
|
|
119
|
+
case 'retry_final_review':
|
|
120
|
+
case 'mark_run_stuck':
|
|
121
|
+
case 'report_run_stuck':
|
|
122
|
+
return executeStuckAction({
|
|
123
|
+
action,
|
|
124
|
+
journal,
|
|
125
|
+
tracker,
|
|
126
|
+
clock: clock ?? SYSTEM_CLOCK,
|
|
127
|
+
reply_poll_ms,
|
|
128
|
+
step: tried,
|
|
129
|
+
})
|
|
130
|
+
case 'stop_for_crashes':
|
|
131
|
+
journal.append({
|
|
132
|
+
kind: 'run_stopped',
|
|
133
|
+
ticket: null,
|
|
134
|
+
role: null,
|
|
135
|
+
content: {
|
|
136
|
+
reason: action.reason,
|
|
137
|
+
role: null,
|
|
138
|
+
billing: false,
|
|
139
|
+
crashed: true,
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
return
|
|
143
|
+
case 'record_usage':
|
|
144
|
+
journal.append({
|
|
145
|
+
kind: 'usage_recorded',
|
|
146
|
+
ticket: action.usage.ticket,
|
|
147
|
+
role: null,
|
|
148
|
+
content: action.usage,
|
|
149
|
+
})
|
|
150
|
+
return
|
|
151
|
+
case 'start_limit_wait':
|
|
152
|
+
case 'wait_for_limit':
|
|
153
|
+
case 'stop_for_billing':
|
|
154
|
+
return executePlanAction({
|
|
155
|
+
action,
|
|
156
|
+
journal,
|
|
157
|
+
tracker,
|
|
158
|
+
clock: clock ?? SYSTEM_CLOCK,
|
|
159
|
+
step: tried,
|
|
160
|
+
})
|
|
161
|
+
case 'start_usage_line_wait':
|
|
162
|
+
case 'wait_for_usage_line':
|
|
163
|
+
return executeUsageLineAction({
|
|
164
|
+
action,
|
|
165
|
+
journal,
|
|
166
|
+
tracker,
|
|
167
|
+
clock: clock ?? SYSTEM_CLOCK,
|
|
168
|
+
read_usage_lines:
|
|
169
|
+
read_usage_lines ??
|
|
170
|
+
(async () => ({
|
|
171
|
+
...DEFAULT_USAGE_LINES,
|
|
172
|
+
[LINE_OF[action.window]]: action.line,
|
|
173
|
+
})),
|
|
174
|
+
step: tried,
|
|
175
|
+
})
|
|
176
|
+
case 'start_repo_wait':
|
|
177
|
+
case 'wait_for_repo':
|
|
178
|
+
return executeRepoWaitAction({
|
|
179
|
+
action,
|
|
180
|
+
journal,
|
|
181
|
+
tracker,
|
|
182
|
+
clock: clock ?? SYSTEM_CLOCK,
|
|
183
|
+
going_runs,
|
|
184
|
+
step: tried,
|
|
185
|
+
})
|
|
186
|
+
case 'read_intake':
|
|
187
|
+
return readIntake({
|
|
188
|
+
spec_number: action.spec_number,
|
|
189
|
+
journal,
|
|
190
|
+
tracker,
|
|
191
|
+
})
|
|
192
|
+
case 'refuse_intake':
|
|
193
|
+
return refuseIntake({
|
|
194
|
+
spec_number: action.spec_number,
|
|
195
|
+
problems: action.problems,
|
|
196
|
+
journal,
|
|
197
|
+
tracker,
|
|
198
|
+
step: tried,
|
|
199
|
+
})
|
|
200
|
+
case 'mark_already_done':
|
|
201
|
+
case 'close_ticket':
|
|
202
|
+
return executeAlreadyDoneAction({
|
|
203
|
+
action,
|
|
204
|
+
journal,
|
|
205
|
+
tracker,
|
|
206
|
+
step: tried,
|
|
207
|
+
})
|
|
208
|
+
case 'finish_nothing_to_do':
|
|
209
|
+
journal.append({
|
|
210
|
+
kind: 'nothing_to_do',
|
|
211
|
+
ticket: null,
|
|
212
|
+
role: null,
|
|
213
|
+
content: {
|
|
214
|
+
closed_tickets: action.closed_tickets,
|
|
215
|
+
already_done: action.already_done,
|
|
216
|
+
left_out: action.left_out ?? [],
|
|
217
|
+
},
|
|
218
|
+
})
|
|
219
|
+
return
|
|
220
|
+
case 'snapshot_intake': {
|
|
221
|
+
const { spec, tickets, closed_tickets, left_out } = action.snapshot
|
|
222
|
+
journal.append({
|
|
223
|
+
kind: 'spec_snapshot',
|
|
224
|
+
ticket: spec.number,
|
|
225
|
+
role: null,
|
|
226
|
+
content: {
|
|
227
|
+
spec,
|
|
228
|
+
ticket_order: tickets.map((ticket) => ticket.number),
|
|
229
|
+
closed_tickets,
|
|
230
|
+
left_out,
|
|
231
|
+
},
|
|
232
|
+
})
|
|
233
|
+
for (const ticket of tickets) {
|
|
234
|
+
journal.append({
|
|
235
|
+
kind: 'ticket_snapshot',
|
|
236
|
+
ticket: ticket.number,
|
|
237
|
+
role: null,
|
|
238
|
+
content: ticket,
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
case 'invalid_journal':
|
|
244
|
+
case 'done':
|
|
245
|
+
return
|
|
246
|
+
default:
|
|
247
|
+
if (build === undefined) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
`The engine needs a git adapter and an agent launcher to ${action.type}.`
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
if (isFinalReviewAction(action)) {
|
|
253
|
+
return executeFinalReviewAction({
|
|
254
|
+
action,
|
|
255
|
+
context: buildContext({
|
|
256
|
+
journal,
|
|
257
|
+
tracker,
|
|
258
|
+
step: tried,
|
|
259
|
+
...build,
|
|
260
|
+
}),
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
if (action.type === 'write_changeset') {
|
|
264
|
+
return writeChangeset({
|
|
265
|
+
action,
|
|
266
|
+
context: buildContext({
|
|
267
|
+
journal,
|
|
268
|
+
tracker,
|
|
269
|
+
step: tried,
|
|
270
|
+
...build,
|
|
271
|
+
}),
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
if (action.type === 'open_pull_request') {
|
|
275
|
+
return openPullRequest({
|
|
276
|
+
action,
|
|
277
|
+
context: buildContext({
|
|
278
|
+
journal,
|
|
279
|
+
tracker,
|
|
280
|
+
step: tried,
|
|
281
|
+
...build,
|
|
282
|
+
}),
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
if (action.type === 'ship_final_review') {
|
|
286
|
+
return shipFinalReviewIn({
|
|
287
|
+
context: buildContext({
|
|
288
|
+
journal,
|
|
289
|
+
tracker,
|
|
290
|
+
step: tried,
|
|
291
|
+
...build,
|
|
292
|
+
}),
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
return executeBuildAction({
|
|
296
|
+
action,
|
|
297
|
+
journal,
|
|
298
|
+
tracker,
|
|
299
|
+
step: tried,
|
|
300
|
+
...build,
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `check_already_done` step: a test-writer's `already_done` evidence
|
|
3
|
+
* checked in the ticket's worktree, journaled as `already_done_checked`.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from 'node:fs'
|
|
6
|
+
import { join } from 'node:path'
|
|
7
|
+
|
|
8
|
+
import uniq from 'lodash/uniq'
|
|
9
|
+
import uniqBy from 'lodash/uniqBy'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
reportFile,
|
|
13
|
+
testFilesIn,
|
|
14
|
+
ticketWorktree,
|
|
15
|
+
type BuildContext,
|
|
16
|
+
} from './build-context'
|
|
17
|
+
import type { BuildAction } from './decide-build'
|
|
18
|
+
import { leaveOutIn, ticketChanges, watchPrepare } from './worktree-changes'
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
bunTestCommands,
|
|
22
|
+
hasSlowTests,
|
|
23
|
+
testTimeoutOf,
|
|
24
|
+
} from '../config/engine-config'
|
|
25
|
+
import { checkAlreadyDone } from '../gates/already-done-check'
|
|
26
|
+
import { prepareCheck } from '../gates/gate-runner'
|
|
27
|
+
import {
|
|
28
|
+
fileTestsCommand,
|
|
29
|
+
onlyTestFiles,
|
|
30
|
+
runBunTests,
|
|
31
|
+
} from '../gates/test-runner'
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks a test-writer's `already_done` evidence (#495) in the ticket's
|
|
35
|
+
* worktree, then journals the check (`already_done_checked`): where git
|
|
36
|
+
* finds each named commit, next to the commit the worktree started from,
|
|
37
|
+
* and a run of just the named test files that exist, are test files, and
|
|
38
|
+
* are as they are on the base, after the prepare command if the config has
|
|
39
|
+
* one. With a slow command (#564), the named files, slow ones included, run
|
|
40
|
+
* with plain `bun test`. What the check means is `checkAlreadyDone`'s; replay
|
|
41
|
+
* makes a failed one a failed try for the test-writer.
|
|
42
|
+
*/
|
|
43
|
+
export const checkAlreadyDoneIn = async ({
|
|
44
|
+
context,
|
|
45
|
+
action,
|
|
46
|
+
}: {
|
|
47
|
+
context: BuildContext
|
|
48
|
+
action: Extract<BuildAction, { type: 'check_already_done' }>
|
|
49
|
+
}) => {
|
|
50
|
+
const { path } = ticketWorktree({
|
|
51
|
+
state: context.state,
|
|
52
|
+
ticket: action.ticket,
|
|
53
|
+
})
|
|
54
|
+
const commits = []
|
|
55
|
+
for (const { sha } of uniqBy(action.done_by, 'sha')) {
|
|
56
|
+
const place = await context.git.commitOnBase({
|
|
57
|
+
cwd: path,
|
|
58
|
+
sha,
|
|
59
|
+
base: action.base_sha,
|
|
60
|
+
})
|
|
61
|
+
commits.push({ sha, place })
|
|
62
|
+
}
|
|
63
|
+
const files = uniq(
|
|
64
|
+
action.criteria.flatMap(({ tests }) => tests.map(({ file }) => file))
|
|
65
|
+
)
|
|
66
|
+
const missing = files.filter((file) => !existsSync(join(path, file)))
|
|
67
|
+
const test_files = await testFilesIn({ context, cwd: path })
|
|
68
|
+
const changed = (
|
|
69
|
+
await ticketChanges({
|
|
70
|
+
context,
|
|
71
|
+
cwd: path,
|
|
72
|
+
leave_out: await leaveOutIn({
|
|
73
|
+
context,
|
|
74
|
+
cwd: path,
|
|
75
|
+
ticket: action.ticket,
|
|
76
|
+
}),
|
|
77
|
+
})
|
|
78
|
+
).map((change) => change.path)
|
|
79
|
+
const runnable = files.filter(
|
|
80
|
+
(file) =>
|
|
81
|
+
!missing.includes(file) &&
|
|
82
|
+
test_files.includes(file) &&
|
|
83
|
+
!changed.includes(file)
|
|
84
|
+
)
|
|
85
|
+
const prepare =
|
|
86
|
+
runnable.length === 0
|
|
87
|
+
? null
|
|
88
|
+
: await watchPrepare({
|
|
89
|
+
context,
|
|
90
|
+
cwd: path,
|
|
91
|
+
ticket: action.ticket,
|
|
92
|
+
run: () =>
|
|
93
|
+
prepareCheck({ config: context.config, cwd: path }),
|
|
94
|
+
})
|
|
95
|
+
const run =
|
|
96
|
+
runnable.length === 0 || (prepare !== null && !prepare.ok)
|
|
97
|
+
? null
|
|
98
|
+
: await runBunTests({
|
|
99
|
+
cwd: path,
|
|
100
|
+
// With a slow command, the fast command would leave out
|
|
101
|
+
// the slow files named (#564): plain `bun test` runs all.
|
|
102
|
+
commands: hasSlowTests({ config: context.config })
|
|
103
|
+
? [fileTestsCommand({ files: runnable })]
|
|
104
|
+
: bunTestCommands(context).map((command) =>
|
|
105
|
+
onlyTestFiles({ command, files: runnable })
|
|
106
|
+
),
|
|
107
|
+
test_files: runnable,
|
|
108
|
+
timeout_ms: testTimeoutOf({ config: context.config }),
|
|
109
|
+
test_concurrency: context.config.test_concurrency,
|
|
110
|
+
report_file: await reportFile({
|
|
111
|
+
context,
|
|
112
|
+
ticket: action.ticket,
|
|
113
|
+
label: 'already-done',
|
|
114
|
+
}),
|
|
115
|
+
})
|
|
116
|
+
const { ok, problems } = checkAlreadyDone({
|
|
117
|
+
base_sha: action.base_sha,
|
|
118
|
+
criteria_ids: action.criteria_ids,
|
|
119
|
+
criteria: action.criteria,
|
|
120
|
+
commits,
|
|
121
|
+
missing,
|
|
122
|
+
test_files,
|
|
123
|
+
changed,
|
|
124
|
+
prepare,
|
|
125
|
+
run,
|
|
126
|
+
})
|
|
127
|
+
context.journal.append({
|
|
128
|
+
kind: 'already_done_checked',
|
|
129
|
+
ticket: action.ticket,
|
|
130
|
+
role: null,
|
|
131
|
+
content: { ok, problems, base_sha: action.base_sha, tests: run },
|
|
132
|
+
})
|
|
133
|
+
}
|