@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,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The steps the engine journals: the one a ticket is in, or the ones the
|
|
3
|
+
* run itself is running, and the stage a step puts a ticket in.
|
|
4
|
+
*/
|
|
5
|
+
import type { BoardRecord } from './board-vocabulary'
|
|
6
|
+
import { roleWords } from './reduce-board-text'
|
|
7
|
+
import { updateTicket } from './reduce-board-update'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
BASELINE_STEP_TEXT,
|
|
11
|
+
type BoardState,
|
|
12
|
+
type TicketCard,
|
|
13
|
+
type TicketStage,
|
|
14
|
+
} from '../shared/board-state'
|
|
15
|
+
|
|
16
|
+
/** A step's action type in words, when it reads better than its name. */
|
|
17
|
+
const STEP_WORDS: Record<string, string> = {
|
|
18
|
+
install_dependencies: 'installing packages',
|
|
19
|
+
run_baseline_tests: BASELINE_STEP_TEXT,
|
|
20
|
+
run_red_check: 'running the red check',
|
|
21
|
+
run_gates: 'running the checks',
|
|
22
|
+
run_final_gates: 'running the checks',
|
|
23
|
+
run_final_slow_tests: 'running the slow tests',
|
|
24
|
+
join_run_branch: 'joining the run branch',
|
|
25
|
+
push_run_branch: 'pushing the run branch',
|
|
26
|
+
push_final_fixes: 'pushing the run branch',
|
|
27
|
+
write_changeset: 'writing the changeset',
|
|
28
|
+
open_pull_request: 'opening the pull request',
|
|
29
|
+
mark_already_done: 'noting the work is already done',
|
|
30
|
+
close_ticket: 'closing the ticket',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A `step_started`'s step in words: an agent's turn (`<type>:<role>`)
|
|
35
|
+
* waits for its role; any other step as `STEP_WORDS` has it, else its
|
|
36
|
+
* action type.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* stepText({ step: 'run_gates' }) // 'running the checks'
|
|
40
|
+
* stepText({ step: 'follow_up_agent:implementer' }) // 'waiting for the implementer'
|
|
41
|
+
*/
|
|
42
|
+
export const stepText = ({ step }: { step: string }): string => {
|
|
43
|
+
const colon = step.indexOf(':')
|
|
44
|
+
if (colon >= 0) {
|
|
45
|
+
return `waiting for the ${roleWords({ role: step.slice(colon + 1) })}`
|
|
46
|
+
}
|
|
47
|
+
return STEP_WORDS[step] ?? step.replaceAll('_', ' ')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The run's running steps, and the newest of them as its current step. */
|
|
51
|
+
const withRunSteps = ({
|
|
52
|
+
state,
|
|
53
|
+
run_steps,
|
|
54
|
+
}: {
|
|
55
|
+
state: BoardState
|
|
56
|
+
run_steps: BoardState['run_steps']
|
|
57
|
+
}): BoardState => {
|
|
58
|
+
const newest = run_steps.at(-1)
|
|
59
|
+
return {
|
|
60
|
+
...state,
|
|
61
|
+
run_steps,
|
|
62
|
+
current_step: newest
|
|
63
|
+
? { text: newest.text, since: newest.since }
|
|
64
|
+
: null,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** No step running anywhere: the engine ended, or the run stopped. */
|
|
69
|
+
export const withoutSteps = ({ state }: { state: BoardState }): BoardState =>
|
|
70
|
+
withRunSteps({
|
|
71
|
+
state: {
|
|
72
|
+
...state,
|
|
73
|
+
tickets: state.tickets.map((card) =>
|
|
74
|
+
card.current_step === null
|
|
75
|
+
? card
|
|
76
|
+
: { ...card, current_step: null }
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
run_steps: [],
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
/** The stages a step doesn't move a ticket out of. */
|
|
83
|
+
const SETTLED_STAGES = new Set<TicketStage>(['stuck', 'done', 'skipped'])
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The stage a ticket's step puts it in (#549): the red check and the checks
|
|
87
|
+
* are testing, but the checks after joining are still joining; joining and
|
|
88
|
+
* pushing the run branch are joining. Any other step, or a stuck, done or
|
|
89
|
+
* skipped ticket, keeps its stage.
|
|
90
|
+
*/
|
|
91
|
+
const stepStage = ({
|
|
92
|
+
card,
|
|
93
|
+
step,
|
|
94
|
+
}: {
|
|
95
|
+
card: TicketCard
|
|
96
|
+
step: string
|
|
97
|
+
}): TicketStage => {
|
|
98
|
+
if (SETTLED_STAGES.has(card.stage)) return card.stage
|
|
99
|
+
switch (step) {
|
|
100
|
+
case 'run_red_check':
|
|
101
|
+
return 'testing'
|
|
102
|
+
case 'run_gates':
|
|
103
|
+
return card.stage === 'joining' ? 'joining' : 'testing'
|
|
104
|
+
case 'join_run_branch':
|
|
105
|
+
case 'push_run_branch':
|
|
106
|
+
return 'joining'
|
|
107
|
+
default:
|
|
108
|
+
return card.stage
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A step began: it is its ticket's current step, or one of the run's, and
|
|
114
|
+
* it sets the ticket's stage. A new step on the same key (a crash's redo)
|
|
115
|
+
* replaces the one before it.
|
|
116
|
+
*/
|
|
117
|
+
const stepStarted = ({
|
|
118
|
+
state,
|
|
119
|
+
record,
|
|
120
|
+
}: {
|
|
121
|
+
state: BoardState
|
|
122
|
+
record: Extract<BoardRecord, { kind: 'step_started' }>
|
|
123
|
+
}): BoardState => {
|
|
124
|
+
// A replayed journal of an engine that ended has no step running.
|
|
125
|
+
const ended = state.run.engine_ended !== null
|
|
126
|
+
const current_step = {
|
|
127
|
+
text: stepText({ step: record.content.step }),
|
|
128
|
+
since: record.time,
|
|
129
|
+
}
|
|
130
|
+
if (record.ticket !== null) {
|
|
131
|
+
return updateTicket({
|
|
132
|
+
state,
|
|
133
|
+
number: record.ticket,
|
|
134
|
+
update: (card) => ({
|
|
135
|
+
...card,
|
|
136
|
+
stage: stepStage({ card, step: record.content.step }),
|
|
137
|
+
current_step: ended ? card.current_step : current_step,
|
|
138
|
+
// Its next step ends any wait.
|
|
139
|
+
wait: null,
|
|
140
|
+
}),
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
if (ended) return state
|
|
144
|
+
const { key } = record.content
|
|
145
|
+
return withRunSteps({
|
|
146
|
+
state,
|
|
147
|
+
run_steps: [
|
|
148
|
+
...state.run_steps.filter((step) => step.key !== key),
|
|
149
|
+
{ key, ...current_step },
|
|
150
|
+
],
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const stepEnded = ({
|
|
155
|
+
state,
|
|
156
|
+
record,
|
|
157
|
+
}: {
|
|
158
|
+
state: BoardState
|
|
159
|
+
record: Extract<BoardRecord, { kind: 'step_ended' }>
|
|
160
|
+
}): BoardState => {
|
|
161
|
+
if (record.ticket !== null) {
|
|
162
|
+
return updateTicket({
|
|
163
|
+
state,
|
|
164
|
+
number: record.ticket,
|
|
165
|
+
// Between steps now: what it waits for is worked out on the
|
|
166
|
+
// card, unless the engine journals it (`ticket_waiting`).
|
|
167
|
+
update: (card) => ({
|
|
168
|
+
...card,
|
|
169
|
+
current_step: null,
|
|
170
|
+
wait: { for: null, on_ticket: null, since: record.time },
|
|
171
|
+
}),
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
return withRunSteps({
|
|
175
|
+
state,
|
|
176
|
+
run_steps: state.run_steps.filter(
|
|
177
|
+
(step) => step.key !== record.content.key
|
|
178
|
+
),
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** A ticket began to wait between steps, for what the engine says (#559). */
|
|
183
|
+
const ticketWaiting = ({
|
|
184
|
+
state,
|
|
185
|
+
record,
|
|
186
|
+
}: {
|
|
187
|
+
state: BoardState
|
|
188
|
+
record: Extract<BoardRecord, { kind: 'ticket_waiting' }>
|
|
189
|
+
}): BoardState => {
|
|
190
|
+
const { for: waiting_for, on_ticket, since } = record.content
|
|
191
|
+
return updateTicket({
|
|
192
|
+
state,
|
|
193
|
+
number: record.ticket,
|
|
194
|
+
update: (card) => ({
|
|
195
|
+
...card,
|
|
196
|
+
wait: { for: waiting_for, on_ticket, since },
|
|
197
|
+
}),
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The record kinds of a step, and of a wait between steps. */
|
|
202
|
+
export const STEP_KINDS = [
|
|
203
|
+
'step_started',
|
|
204
|
+
'step_ended',
|
|
205
|
+
'ticket_waiting',
|
|
206
|
+
] as const
|
|
207
|
+
|
|
208
|
+
type StepRecord = Extract<BoardRecord, { kind: (typeof STEP_KINDS)[number] }>
|
|
209
|
+
|
|
210
|
+
/** Applies one record of STEP_KINDS to the board. */
|
|
211
|
+
export const applyStepKind = ({
|
|
212
|
+
state,
|
|
213
|
+
record,
|
|
214
|
+
}: {
|
|
215
|
+
state: BoardState
|
|
216
|
+
record: StepRecord
|
|
217
|
+
}): BoardState => {
|
|
218
|
+
switch (record.kind) {
|
|
219
|
+
case 'step_started':
|
|
220
|
+
return stepStarted({ state, record })
|
|
221
|
+
case 'step_ended':
|
|
222
|
+
return stepEnded({ state, record })
|
|
223
|
+
case 'ticket_waiting':
|
|
224
|
+
return ticketWaiting({ state, record })
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Words for the reducer: short pure functions that turn ids, counts and
|
|
3
|
+
* record fields into the texts a card or row shows. They hold no board
|
|
4
|
+
* state.
|
|
5
|
+
*/
|
|
6
|
+
import type { BoardRecord } from './board-vocabulary'
|
|
7
|
+
|
|
8
|
+
import type { FindingCounts } from '../shared/board-state'
|
|
9
|
+
|
|
10
|
+
/** Longest single "tried" line kept in state. */
|
|
11
|
+
export const LINE_MAX = 240
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A commit's short sha, as git and GitHub show it.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* shortSha({ sha: '3559c25f5a1b' }) // '3559c25'
|
|
18
|
+
*/
|
|
19
|
+
export const shortSha = ({ sha }: { sha: string }): string => sha.slice(0, 7)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* An already-done ticket's activity (#484): calm, with the commits that
|
|
23
|
+
* did its work.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* alreadyDoneText({ shas: ['3559c25f5a1b'] }) // 'already done · 3559c25'
|
|
27
|
+
*/
|
|
28
|
+
export const alreadyDoneText = ({ shas }: { shas: string[] }): string =>
|
|
29
|
+
['already done', ...shas.map((sha) => shortSha({ sha }))].join(' · ')
|
|
30
|
+
|
|
31
|
+
/** Cuts `text` to `max` characters, ending in "…" when it was longer. */
|
|
32
|
+
export const clip = ({ text, max }: { text: string; max: number }): string =>
|
|
33
|
+
text.length > max ? `${text.slice(0, max - 1)}…` : text
|
|
34
|
+
|
|
35
|
+
export const firstLine = ({ text }: { text: string }): string =>
|
|
36
|
+
clip({ text: text.split('\n')[0] ?? '', max: LINE_MAX })
|
|
37
|
+
|
|
38
|
+
/** How an agent turn failed, in words: `agent_failed`'s `failure`. */
|
|
39
|
+
const FAILURES: Record<string, string> = {
|
|
40
|
+
agent: 'failed',
|
|
41
|
+
result: 'gave no usable result',
|
|
42
|
+
guard: "broke its role's rules (the changes were undone)",
|
|
43
|
+
engine: 'could not run (an engine-side failure; a fresh agent starts)',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** An `agent_failed` failure kind in words. */
|
|
47
|
+
export const failureText = ({ failure }: { failure: string }): string =>
|
|
48
|
+
FAILURES[failure] ?? `failed (${failure})`
|
|
49
|
+
|
|
50
|
+
/** Issue numbers as `#12`, `#12 and #13`, or `#12, #13 and #14`. */
|
|
51
|
+
export const numbersText = ({ numbers }: { numbers: number[] }): string => {
|
|
52
|
+
const refs = numbers.map((number) => `#${number}`)
|
|
53
|
+
const last = refs.at(-1) ?? ''
|
|
54
|
+
return refs.length <= 1
|
|
55
|
+
? last
|
|
56
|
+
: `${refs.slice(0, -1).join(', ')} and ${last}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A role in words: a final review lens's role (`security-lens`) reads
|
|
61
|
+
* "security lens"; any other role as it is.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* roleWords({ role: 'security-lens' }) // 'security lens'
|
|
65
|
+
*/
|
|
66
|
+
export const roleWords = ({ role }: { role: string }): string =>
|
|
67
|
+
role.endsWith('-lens') ? `${role.slice(0, -'-lens'.length)} lens` : role
|
|
68
|
+
|
|
69
|
+
/** A ticket review's findings counted by severity. */
|
|
70
|
+
export const countFindings = ({
|
|
71
|
+
findings,
|
|
72
|
+
}: {
|
|
73
|
+
findings: { severity: 'blocker' | 'should_fix' | 'nit' }[]
|
|
74
|
+
}): FindingCounts => ({
|
|
75
|
+
blocker: findings.filter((finding) => finding.severity === 'blocker')
|
|
76
|
+
.length,
|
|
77
|
+
should_fix: findings.filter((finding) => finding.severity === 'should_fix')
|
|
78
|
+
.length,
|
|
79
|
+
nit: findings.filter((finding) => finding.severity === 'nit').length,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const plural = ({ count, word }: { count: number; word: string }) =>
|
|
83
|
+
`${count} ${word}${count === 1 ? '' : 's'}`
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A ticket review's finding counts in words, such as "1 blocker, 2
|
|
87
|
+
* should-fix, 2 nits". Nits are left out unless `nits` is set; `null` when
|
|
88
|
+
* nothing is left to say.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* reviewCountsText({ findings: { blocker: 2, should_fix: 1, nit: 1 }, nits: false })
|
|
92
|
+
* // '2 blockers, 1 should-fix'
|
|
93
|
+
*/
|
|
94
|
+
export const reviewCountsText = ({
|
|
95
|
+
findings,
|
|
96
|
+
nits,
|
|
97
|
+
}: {
|
|
98
|
+
findings: FindingCounts
|
|
99
|
+
nits: boolean
|
|
100
|
+
}): string | null => {
|
|
101
|
+
const parts = [
|
|
102
|
+
findings.blocker > 0
|
|
103
|
+
? plural({ count: findings.blocker, word: 'blocker' })
|
|
104
|
+
: null,
|
|
105
|
+
findings.should_fix > 0 ? `${findings.should_fix} should-fix` : null,
|
|
106
|
+
nits && findings.nit > 0
|
|
107
|
+
? plural({ count: findings.nit, word: 'nit' })
|
|
108
|
+
: null,
|
|
109
|
+
].filter((part) => part !== null)
|
|
110
|
+
return parts.length > 0 ? parts.join(', ') : null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** What happened when a ticket was sent back onto the run branch, in words. */
|
|
114
|
+
export const rebasedText = ({
|
|
115
|
+
content,
|
|
116
|
+
}: {
|
|
117
|
+
content: Extract<BoardRecord, { kind: 'ticket_rebased' }>['content']
|
|
118
|
+
}): string => {
|
|
119
|
+
if (content.cause === 'join_gates') {
|
|
120
|
+
return 'The checks failed after joining; fixing on top of the run branch'
|
|
121
|
+
}
|
|
122
|
+
const files = [...content.tests, ...content.code]
|
|
123
|
+
return files.length === 0
|
|
124
|
+
? 'Clashed with the run branch; fixing on top of it'
|
|
125
|
+
: `Clashed with the run branch in ${files.join(', ')}`
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** A test sent back to the test-writer after a rebase (#489), in words. */
|
|
129
|
+
export const testsSentBackText = ({
|
|
130
|
+
content,
|
|
131
|
+
}: {
|
|
132
|
+
content: Extract<BoardRecord, { kind: 'tests_sent_back' }>['content']
|
|
133
|
+
}): string => {
|
|
134
|
+
const test = [content.bad_test.file, content.bad_test.name]
|
|
135
|
+
.filter(Boolean)
|
|
136
|
+
.join(' > ')
|
|
137
|
+
const joined = content.joined.map(({ ticket }) => `#${ticket}`).join(', ')
|
|
138
|
+
return `Test update ${content.round}: ${test || 'a test'} is wrong on the run branch${joined === '' ? '' : ` after ${joined} joined`}; back to the test-writer`
|
|
139
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A ticket's checks while it builds: the red check, commits, the leftover
|
|
3
|
+
* scan, and the checks (the final review's too).
|
|
4
|
+
*/
|
|
5
|
+
import type { BoardRecord } from './board-vocabulary'
|
|
6
|
+
import {
|
|
7
|
+
finalTried,
|
|
8
|
+
testCounts,
|
|
9
|
+
updateTicket,
|
|
10
|
+
withTried,
|
|
11
|
+
} from './reduce-board-update'
|
|
12
|
+
|
|
13
|
+
import type { BoardState } from '../shared/board-state'
|
|
14
|
+
|
|
15
|
+
const gatesRun = ({
|
|
16
|
+
state,
|
|
17
|
+
record,
|
|
18
|
+
}: {
|
|
19
|
+
state: BoardState
|
|
20
|
+
record: Extract<BoardRecord, { kind: 'gates_run' }>
|
|
21
|
+
}): BoardState => {
|
|
22
|
+
const { ok, target, checks } = record.content
|
|
23
|
+
const failed = checks
|
|
24
|
+
.filter((check) => !check.ok)
|
|
25
|
+
.map((check) => check.name)
|
|
26
|
+
.join(', ')
|
|
27
|
+
const afterJoin = target === 'run_branch'
|
|
28
|
+
// The final review's slow tests, after round 1's lenses (#563).
|
|
29
|
+
if (target === 'final_slow') {
|
|
30
|
+
return ok
|
|
31
|
+
? state
|
|
32
|
+
: finalTried({
|
|
33
|
+
state,
|
|
34
|
+
line: `The final review's slow tests failed: ${failed || 'no detail'}`,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
// The final review's fixes are checked on the run branch, with no ticket.
|
|
38
|
+
if (record.ticket === null) {
|
|
39
|
+
return ok
|
|
40
|
+
? state
|
|
41
|
+
: finalTried({
|
|
42
|
+
state,
|
|
43
|
+
line: `The checks failed on the final review's fixes: ${failed || 'no detail'}`,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
return updateTicket({
|
|
47
|
+
state,
|
|
48
|
+
number: record.ticket,
|
|
49
|
+
update: (card) => {
|
|
50
|
+
if (ok) {
|
|
51
|
+
return afterJoin
|
|
52
|
+
? { ...card, activity: 'checks passed after joining' }
|
|
53
|
+
: {
|
|
54
|
+
...card,
|
|
55
|
+
step: 4,
|
|
56
|
+
open_check: null,
|
|
57
|
+
fix_round: 0,
|
|
58
|
+
activity: 'checks passed',
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
...card,
|
|
63
|
+
step: afterJoin ? card.step : 3,
|
|
64
|
+
open_check: afterJoin ? card.open_check : 'gates',
|
|
65
|
+
activity: afterJoin
|
|
66
|
+
? 'checks failed after joining'
|
|
67
|
+
: 'checks failed',
|
|
68
|
+
tried: withTried({
|
|
69
|
+
tried: card.tried,
|
|
70
|
+
line: `${afterJoin ? 'After joining, the' : 'The'} checks failed: ${failed || 'no detail'}`,
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The record kinds of a ticket's checks. */
|
|
78
|
+
export const TICKET_CHECKS_KINDS = [
|
|
79
|
+
'red_check',
|
|
80
|
+
'leftover_scan',
|
|
81
|
+
'commit_made',
|
|
82
|
+
'gates_run',
|
|
83
|
+
] as const
|
|
84
|
+
|
|
85
|
+
type TicketChecksRecord = Extract<
|
|
86
|
+
BoardRecord,
|
|
87
|
+
{ kind: (typeof TICKET_CHECKS_KINDS)[number] }
|
|
88
|
+
>
|
|
89
|
+
|
|
90
|
+
/** Applies one record of TICKET_CHECKS_KINDS to the board. */
|
|
91
|
+
export const applyTicketChecksKind = ({
|
|
92
|
+
state,
|
|
93
|
+
record,
|
|
94
|
+
}: {
|
|
95
|
+
state: BoardState
|
|
96
|
+
record: TicketChecksRecord
|
|
97
|
+
}): BoardState => {
|
|
98
|
+
const { ticket } = record
|
|
99
|
+
switch (record.kind) {
|
|
100
|
+
case 'red_check':
|
|
101
|
+
return updateTicket({
|
|
102
|
+
state,
|
|
103
|
+
number: ticket,
|
|
104
|
+
update: (card) => ({
|
|
105
|
+
...card,
|
|
106
|
+
step: record.content.ok ? 2 : 1,
|
|
107
|
+
open_check: record.content.ok ? null : 'red_check',
|
|
108
|
+
fix_round: record.content.ok ? 0 : card.fix_round,
|
|
109
|
+
activity: record.content.ok
|
|
110
|
+
? 'red check passed'
|
|
111
|
+
: 'red check failed',
|
|
112
|
+
tests: record.content.tests
|
|
113
|
+
? testCounts({ cases: record.content.tests.cases })
|
|
114
|
+
: card.tests,
|
|
115
|
+
tried: record.content.ok
|
|
116
|
+
? card.tried
|
|
117
|
+
: withTried({
|
|
118
|
+
tried: card.tried,
|
|
119
|
+
line: `The red check failed: ${record.content.problems[0] ?? 'no detail'}`,
|
|
120
|
+
}),
|
|
121
|
+
}),
|
|
122
|
+
})
|
|
123
|
+
case 'leftover_scan':
|
|
124
|
+
if (record.content.hits.length === 0) return state
|
|
125
|
+
if (ticket === null) {
|
|
126
|
+
return finalTried({
|
|
127
|
+
state,
|
|
128
|
+
line: `The leftover scan found ${record.content.hits.map((hit) => hit.path).join(', ')} in the final review's fixes`,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
return updateTicket({
|
|
132
|
+
state,
|
|
133
|
+
number: ticket,
|
|
134
|
+
update: (card) => ({
|
|
135
|
+
...card,
|
|
136
|
+
activity: 'leftovers found',
|
|
137
|
+
tried: withTried({
|
|
138
|
+
tried: card.tried,
|
|
139
|
+
line: `The leftover scan found ${record.content.hits.map((hit) => hit.path).join(', ')}`,
|
|
140
|
+
}),
|
|
141
|
+
}),
|
|
142
|
+
})
|
|
143
|
+
case 'commit_made':
|
|
144
|
+
return updateTicket({
|
|
145
|
+
state,
|
|
146
|
+
number: ticket,
|
|
147
|
+
update: (card) => {
|
|
148
|
+
switch (record.content.stage) {
|
|
149
|
+
case 'green':
|
|
150
|
+
return {
|
|
151
|
+
...card,
|
|
152
|
+
step: 4,
|
|
153
|
+
activity: 'code committed',
|
|
154
|
+
}
|
|
155
|
+
case 'fix':
|
|
156
|
+
return {
|
|
157
|
+
...card,
|
|
158
|
+
activity: 'review fixes committed',
|
|
159
|
+
}
|
|
160
|
+
default:
|
|
161
|
+
return { ...card, activity: 'tests committed' }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
case 'gates_run':
|
|
166
|
+
return gatesRun({ state, record })
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A ticket's way onto the run branch: joining, being sent back to fix on top
|
|
3
|
+
* of it, being pushed, or being found already done.
|
|
4
|
+
*/
|
|
5
|
+
import type { BoardRecord } from './board-vocabulary'
|
|
6
|
+
import {
|
|
7
|
+
alreadyDoneText,
|
|
8
|
+
rebasedText,
|
|
9
|
+
testsSentBackText,
|
|
10
|
+
} from './reduce-board-text'
|
|
11
|
+
import { updateTicket, withTried } from './reduce-board-update'
|
|
12
|
+
|
|
13
|
+
import { ALL_STEPS_DONE, type BoardState } from '../shared/board-state'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A ticket sent back onto the run branch is fixed on top of it: its tests
|
|
17
|
+
* first if test files clashed, else its code; then the checks and a fresh
|
|
18
|
+
* review, whose fix rounds count from zero again.
|
|
19
|
+
*/
|
|
20
|
+
const ticketRebased = ({
|
|
21
|
+
state,
|
|
22
|
+
record,
|
|
23
|
+
}: {
|
|
24
|
+
state: BoardState
|
|
25
|
+
record: Extract<BoardRecord, { kind: 'ticket_rebased' }>
|
|
26
|
+
}): BoardState =>
|
|
27
|
+
updateTicket({
|
|
28
|
+
state,
|
|
29
|
+
number: record.ticket,
|
|
30
|
+
update: (card) => ({
|
|
31
|
+
...card,
|
|
32
|
+
stage: 'fixing',
|
|
33
|
+
step: record.content.tests.length > 0 ? 0 : 2,
|
|
34
|
+
role: null,
|
|
35
|
+
fix_round: 0,
|
|
36
|
+
// The engine starts the ticket's review loop over on top of the
|
|
37
|
+
// run branch: a fresh reviewer, fresh review fix rounds.
|
|
38
|
+
review_fix_round: 0,
|
|
39
|
+
findings: null,
|
|
40
|
+
open_check: null,
|
|
41
|
+
failed_turn: null,
|
|
42
|
+
activity: 'fixing on the run branch',
|
|
43
|
+
tried: withTried({
|
|
44
|
+
tried: card.tried,
|
|
45
|
+
line: rebasedText({ content: record.content }),
|
|
46
|
+
}),
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/** The record kinds of a ticket's way onto the run branch. */
|
|
51
|
+
export const TICKET_JOIN_KINDS = [
|
|
52
|
+
'ticket_joined',
|
|
53
|
+
'ticket_rebased',
|
|
54
|
+
'tests_sent_back',
|
|
55
|
+
'run_branch_pushed',
|
|
56
|
+
'ticket_already_done',
|
|
57
|
+
'ticket_closed',
|
|
58
|
+
] as const
|
|
59
|
+
|
|
60
|
+
type TicketJoinRecord = Extract<
|
|
61
|
+
BoardRecord,
|
|
62
|
+
{ kind: (typeof TICKET_JOIN_KINDS)[number] }
|
|
63
|
+
>
|
|
64
|
+
|
|
65
|
+
/** Applies one record of TICKET_JOIN_KINDS to the board. */
|
|
66
|
+
export const applyTicketJoinKind = ({
|
|
67
|
+
state,
|
|
68
|
+
record,
|
|
69
|
+
}: {
|
|
70
|
+
state: BoardState
|
|
71
|
+
record: TicketJoinRecord
|
|
72
|
+
}): BoardState => {
|
|
73
|
+
const { ticket } = record
|
|
74
|
+
switch (record.kind) {
|
|
75
|
+
case 'ticket_joined':
|
|
76
|
+
return updateTicket({
|
|
77
|
+
state,
|
|
78
|
+
number: ticket,
|
|
79
|
+
// Joined, it is done once its checks there pass and the run
|
|
80
|
+
// branch is pushed.
|
|
81
|
+
update: (card) =>
|
|
82
|
+
record.content.ok
|
|
83
|
+
? {
|
|
84
|
+
...card,
|
|
85
|
+
stage: 'joining',
|
|
86
|
+
step: ALL_STEPS_DONE,
|
|
87
|
+
role: null,
|
|
88
|
+
activity: 'joined',
|
|
89
|
+
}
|
|
90
|
+
: {
|
|
91
|
+
...card,
|
|
92
|
+
activity: 'join failed',
|
|
93
|
+
tried: withTried({
|
|
94
|
+
tried: card.tried,
|
|
95
|
+
line: `Joining the run branch failed: ${record.content.error ?? 'no detail'}`,
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
case 'ticket_rebased':
|
|
100
|
+
return ticketRebased({ state, record })
|
|
101
|
+
case 'tests_sent_back':
|
|
102
|
+
return updateTicket({
|
|
103
|
+
state,
|
|
104
|
+
number: ticket,
|
|
105
|
+
update: (card) => ({
|
|
106
|
+
...card,
|
|
107
|
+
stage: 'fixing',
|
|
108
|
+
step: 0,
|
|
109
|
+
role: null,
|
|
110
|
+
activity: 'updating tests for the run branch',
|
|
111
|
+
tried: withTried({
|
|
112
|
+
tried: card.tried,
|
|
113
|
+
line: testsSentBackText({ content: record.content }),
|
|
114
|
+
}),
|
|
115
|
+
}),
|
|
116
|
+
})
|
|
117
|
+
case 'run_branch_pushed':
|
|
118
|
+
return updateTicket({
|
|
119
|
+
state,
|
|
120
|
+
number: ticket,
|
|
121
|
+
update: (card) =>
|
|
122
|
+
card.stage === 'joining' || card.stage === 'done'
|
|
123
|
+
? { ...card, stage: 'done', activity: 'pushed' }
|
|
124
|
+
: card,
|
|
125
|
+
})
|
|
126
|
+
// Done, not stuck (#484): its work was already on the base branch.
|
|
127
|
+
case 'ticket_already_done':
|
|
128
|
+
return updateTicket({
|
|
129
|
+
state,
|
|
130
|
+
number: ticket,
|
|
131
|
+
update: (card) => ({
|
|
132
|
+
...card,
|
|
133
|
+
stage: 'done',
|
|
134
|
+
step: ALL_STEPS_DONE,
|
|
135
|
+
role: null,
|
|
136
|
+
open_check: null,
|
|
137
|
+
activity: alreadyDoneText({ shas: record.content.shas }),
|
|
138
|
+
}),
|
|
139
|
+
})
|
|
140
|
+
// Its closing changes nothing the board shows.
|
|
141
|
+
case 'ticket_closed':
|
|
142
|
+
return state
|
|
143
|
+
}
|
|
144
|
+
}
|