@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,194 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs'
|
|
2
|
+
import { mkdir, mkdtemp, rm } from 'node:fs/promises'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { $ } from 'bun'
|
|
7
|
+
|
|
8
|
+
import { afterEach, beforeEach, expect } from 'bun:test'
|
|
9
|
+
|
|
10
|
+
import type { FakeMuninn } from './fake-muninn'
|
|
11
|
+
import { git } from './practice-repo'
|
|
12
|
+
|
|
13
|
+
import { runSetup } from '../cli/setup'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `luca setup` end to end (seam 4): a throwaway repo with a local bare
|
|
17
|
+
* `origin`, a fake GitHub (labels, the `gh` login, the repo, and its issue
|
|
18
|
+
* links), and a fake MuninnDB. Shared by `cli/setup.test.ts` and
|
|
19
|
+
* `cli/setup-checks.test.ts`: each calls `useSetupRepo()` once, and reads
|
|
20
|
+
* `root`, `repo`, `origin`, and `logs` as they stand in the running test.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export const CONFIG_PATH = join('.luca', 'config.json')
|
|
24
|
+
|
|
25
|
+
/** The keys a new-style engine config may have. */
|
|
26
|
+
export const NEW_CONFIG_KEYS = [
|
|
27
|
+
'checks',
|
|
28
|
+
'test_file_patterns',
|
|
29
|
+
'test_setup_files',
|
|
30
|
+
'rule_files',
|
|
31
|
+
'muninn',
|
|
32
|
+
'run_budget_tokens',
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
/** A `package.json` shaped like tmnb's: a bun test, a vitest file, and more. */
|
|
36
|
+
export const TMNB_PACKAGE = JSON.stringify(
|
|
37
|
+
{
|
|
38
|
+
name: 'tmnb',
|
|
39
|
+
private: true,
|
|
40
|
+
scripts: {
|
|
41
|
+
dev: 'next dev',
|
|
42
|
+
test: 'bun test',
|
|
43
|
+
'test:workers': 'vitest run --config vitest.workers.config.ts',
|
|
44
|
+
'type-check': 'tsc --noEmit',
|
|
45
|
+
lint: 'eslint .',
|
|
46
|
+
'syncpack:check': 'syncpack lint',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
null,
|
|
50
|
+
4
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
/** An old-Luca config: its own keys, and the vault to keep. */
|
|
54
|
+
export const OLD_LUCA_CONFIG = JSON.stringify(
|
|
55
|
+
{
|
|
56
|
+
lucaVersion: '13.1.0-alpha.0',
|
|
57
|
+
oversight: 'full-auto',
|
|
58
|
+
preferences: { schemaVersion: 1 },
|
|
59
|
+
muninn: {
|
|
60
|
+
vault: 'tmnb',
|
|
61
|
+
todoBacklog: { vault: 'tmnb', rootId: 'x' },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
null,
|
|
65
|
+
2
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
type FakeLabel = { name: string; color: string; description: string }
|
|
69
|
+
|
|
70
|
+
/** A fake GitHub side for the repo: labels, the login, and issue links. */
|
|
71
|
+
export const fakeGitHub = ({
|
|
72
|
+
labels = [],
|
|
73
|
+
login = 'asibilia',
|
|
74
|
+
repo_name = 'asibilia/tmnb',
|
|
75
|
+
sub_issues = true,
|
|
76
|
+
dependencies = true,
|
|
77
|
+
}: {
|
|
78
|
+
labels?: FakeLabel[]
|
|
79
|
+
login?: string | null
|
|
80
|
+
repo_name?: string | null
|
|
81
|
+
sub_issues?: boolean
|
|
82
|
+
dependencies?: boolean
|
|
83
|
+
} = {}) => {
|
|
84
|
+
const store = labels.map((label) => ({ ...label }))
|
|
85
|
+
const created: string[] = []
|
|
86
|
+
return {
|
|
87
|
+
github: {
|
|
88
|
+
login: async () => login,
|
|
89
|
+
githubRepo: async () => repo_name,
|
|
90
|
+
issueLinks: async () => ({ sub_issues, dependencies }),
|
|
91
|
+
listLabels: async () => store.map(({ name }) => name),
|
|
92
|
+
createLabel: async ({ name, color, description }: FakeLabel) => {
|
|
93
|
+
if (store.some((label) => label.name === name)) {
|
|
94
|
+
throw new Error(`label ${name} already exists`)
|
|
95
|
+
}
|
|
96
|
+
created.push(name)
|
|
97
|
+
store.push({ name, color, description })
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
/** Every label now, as stored. */
|
|
101
|
+
labels: () => store.map((label) => ({ ...label })),
|
|
102
|
+
/** The names of the labels setup created, oldest first. */
|
|
103
|
+
created: () => [...created],
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export let root = ''
|
|
108
|
+
export let repo = ''
|
|
109
|
+
export let origin = ''
|
|
110
|
+
export const logs: string[] = []
|
|
111
|
+
const log = (line: string) => {
|
|
112
|
+
logs.push(line)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Makes fresh throwaway folders before each test, and removes them after. */
|
|
116
|
+
export const useSetupRepo = () => {
|
|
117
|
+
beforeEach(async () => {
|
|
118
|
+
root = realpathSync(await mkdtemp(join(tmpdir(), 'setup-repo-')))
|
|
119
|
+
repo = join(root, 'repo')
|
|
120
|
+
origin = join(root, 'origin.git')
|
|
121
|
+
logs.length = 0
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
afterEach(async () => {
|
|
125
|
+
await rm(root, { recursive: true, force: true })
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A throwaway repo with these files in its first commit and a local bare
|
|
131
|
+
* `origin`. `main` is pushed unless `push` is false.
|
|
132
|
+
*/
|
|
133
|
+
export const makeRepo = async ({
|
|
134
|
+
files,
|
|
135
|
+
push = true,
|
|
136
|
+
}: {
|
|
137
|
+
files: Record<string, string>
|
|
138
|
+
push?: boolean
|
|
139
|
+
}) => {
|
|
140
|
+
await $`git init -q --bare -b main ${origin}`.quiet()
|
|
141
|
+
await $`git init -q -b main ${repo}`.quiet()
|
|
142
|
+
const hooks = join(root, 'no-hooks')
|
|
143
|
+
await mkdir(hooks)
|
|
144
|
+
await git(repo, 'config', 'user.name', 'Practice')
|
|
145
|
+
await git(repo, 'config', 'user.email', 'practice@example.com')
|
|
146
|
+
await git(repo, 'config', 'commit.gpgsign', 'false')
|
|
147
|
+
await git(repo, 'config', 'core.hooksPath', hooks)
|
|
148
|
+
await Bun.write(join(repo, 'README.md'), '# Throwaway\n')
|
|
149
|
+
for (const [path, content] of Object.entries(files)) {
|
|
150
|
+
await Bun.write(join(repo, path), content)
|
|
151
|
+
}
|
|
152
|
+
await git(repo, 'add', '-A')
|
|
153
|
+
await git(repo, 'commit', '-q', '-m', 'initial')
|
|
154
|
+
await git(repo, 'remote', 'add', 'origin', origin)
|
|
155
|
+
if (push) await git(repo, 'push', '-q', 'origin', 'main')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const setup = async ({
|
|
159
|
+
github,
|
|
160
|
+
memory,
|
|
161
|
+
}: {
|
|
162
|
+
github: ReturnType<typeof fakeGitHub>['github']
|
|
163
|
+
memory: FakeMuninn
|
|
164
|
+
}) => runSetup({ repo, github, memory, log })
|
|
165
|
+
|
|
166
|
+
/** Everything the command printed, its end message included. */
|
|
167
|
+
export const printed = (end: { message: string }): string =>
|
|
168
|
+
[...logs, end.message].join('\n')
|
|
169
|
+
|
|
170
|
+
export const readConfigText = (): Promise<string> =>
|
|
171
|
+
Bun.file(join(repo, CONFIG_PATH)).text()
|
|
172
|
+
|
|
173
|
+
export const checkNamed = (
|
|
174
|
+
end: Awaited<ReturnType<typeof setup>>,
|
|
175
|
+
name: string
|
|
176
|
+
): { name: string; status: string; detail: string } => {
|
|
177
|
+
const found = end.checks.find((check) => check.name === name)
|
|
178
|
+
if (found === undefined) throw new Error(`No check named ${name}`)
|
|
179
|
+
return found
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Checks that a check is a to-do whose fix is printed. */
|
|
183
|
+
export const expectToDo = (
|
|
184
|
+
end: Awaited<ReturnType<typeof setup>>,
|
|
185
|
+
name: string
|
|
186
|
+
) => {
|
|
187
|
+
const check = checkNamed(end, name)
|
|
188
|
+
expect(check.status).toBe('todo')
|
|
189
|
+
expect(check.detail.trim()).not.toBe('')
|
|
190
|
+
expect(printed(end)).toContain(check.detail)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const commitCount = async (cwd: string): Promise<string> =>
|
|
194
|
+
(await git(cwd, 'rev-list', '--count', 'main')).trim()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal entry builders for the scheduler's step records: a step started
|
|
3
|
+
* and a step ended.
|
|
4
|
+
*/
|
|
5
|
+
import type { AgentRole } from '../agents/role-results'
|
|
6
|
+
import type { JournalEntry } from '../journal/journal-record'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The scheduler started a step: `step` is its action type, plus `:<role>`
|
|
10
|
+
* for an agent's turn. `key` defaults to the ticket's number, or `run`.
|
|
11
|
+
*/
|
|
12
|
+
export const stepStarted = ({
|
|
13
|
+
ticket,
|
|
14
|
+
step,
|
|
15
|
+
key,
|
|
16
|
+
role,
|
|
17
|
+
first_seq,
|
|
18
|
+
}: {
|
|
19
|
+
ticket: number | null
|
|
20
|
+
step: string
|
|
21
|
+
key?: string
|
|
22
|
+
role?: AgentRole
|
|
23
|
+
/** The step's first try, on a redo. Defaults to `null`. */
|
|
24
|
+
first_seq?: number
|
|
25
|
+
}): JournalEntry => ({
|
|
26
|
+
kind: 'step_started',
|
|
27
|
+
ticket,
|
|
28
|
+
role: role ?? null,
|
|
29
|
+
content: {
|
|
30
|
+
key: key ?? (ticket === null ? 'run' : String(ticket)),
|
|
31
|
+
step,
|
|
32
|
+
first_seq: first_seq ?? null,
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
/** The scheduler saw a step settle. `key` as in `stepStarted`. */
|
|
37
|
+
export const stepEnded = ({
|
|
38
|
+
ticket,
|
|
39
|
+
step,
|
|
40
|
+
key,
|
|
41
|
+
role,
|
|
42
|
+
}: {
|
|
43
|
+
ticket: number | null
|
|
44
|
+
step: string
|
|
45
|
+
key?: string
|
|
46
|
+
role?: AgentRole
|
|
47
|
+
}): JournalEntry => ({
|
|
48
|
+
kind: 'step_ended',
|
|
49
|
+
ticket,
|
|
50
|
+
role: role ?? null,
|
|
51
|
+
content: {
|
|
52
|
+
key: key ?? (ticket === null ? 'run' : String(ticket)),
|
|
53
|
+
step,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal entry builders for the steps along a ticket's spine: the red check,
|
|
3
|
+
* the leftover scan, commits, gates, resets, the join and its rebase, the
|
|
4
|
+
* push, a stuck ticket, and `ticketBuilt`, every step of one ticket passing.
|
|
5
|
+
*/
|
|
6
|
+
import { implemented, reviewed, testsWritten } from './agent-turn-fixtures'
|
|
7
|
+
import {
|
|
8
|
+
baselineTests,
|
|
9
|
+
emptyTestRun,
|
|
10
|
+
RUN_BRANCH,
|
|
11
|
+
ticketWorktreeCreated,
|
|
12
|
+
} from './run-fixtures'
|
|
13
|
+
|
|
14
|
+
import type { BadTest } from '../agents/role-results'
|
|
15
|
+
import type { TestCase } from '../gates/gate-schemas'
|
|
16
|
+
import type {
|
|
17
|
+
CommitStage,
|
|
18
|
+
JournalEntry,
|
|
19
|
+
StuckReason,
|
|
20
|
+
} from '../journal/journal-record'
|
|
21
|
+
|
|
22
|
+
export const redCheck = ({
|
|
23
|
+
ticket,
|
|
24
|
+
ok,
|
|
25
|
+
}: {
|
|
26
|
+
ticket: number
|
|
27
|
+
ok: boolean
|
|
28
|
+
}): JournalEntry => ({
|
|
29
|
+
kind: 'red_check',
|
|
30
|
+
ticket,
|
|
31
|
+
role: null,
|
|
32
|
+
content: {
|
|
33
|
+
ok,
|
|
34
|
+
problems: ok ? [] : ['"sum adds two numbers" passes already'],
|
|
35
|
+
notes: [],
|
|
36
|
+
tests: {
|
|
37
|
+
...emptyTestRun(),
|
|
38
|
+
output: ok ? '1 fail' : '(pass) sum adds two numbers',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export const leftoverScan = ({
|
|
44
|
+
ticket,
|
|
45
|
+
stage,
|
|
46
|
+
hits,
|
|
47
|
+
}: {
|
|
48
|
+
/** `null` for the final review. */
|
|
49
|
+
ticket: number | null
|
|
50
|
+
stage: CommitStage
|
|
51
|
+
hits?: { path: string; reason: string }[]
|
|
52
|
+
}): JournalEntry => ({
|
|
53
|
+
kind: 'leftover_scan',
|
|
54
|
+
ticket,
|
|
55
|
+
role: null,
|
|
56
|
+
content: { stage, hits: hits ?? [] },
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export const commitMade = ({
|
|
60
|
+
ticket,
|
|
61
|
+
stage,
|
|
62
|
+
sha,
|
|
63
|
+
files,
|
|
64
|
+
}: {
|
|
65
|
+
/** `null` for the final review. */
|
|
66
|
+
ticket: number | null
|
|
67
|
+
stage: CommitStage
|
|
68
|
+
/** Defaults to `<stage>-sha`. */
|
|
69
|
+
sha?: string
|
|
70
|
+
files?: string[]
|
|
71
|
+
}): JournalEntry => ({
|
|
72
|
+
kind: 'commit_made',
|
|
73
|
+
ticket,
|
|
74
|
+
role: null,
|
|
75
|
+
content: {
|
|
76
|
+
stage,
|
|
77
|
+
sha: sha ?? `${stage}-sha`,
|
|
78
|
+
message: stage,
|
|
79
|
+
files: files ?? [],
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export const gatesRun = ({
|
|
84
|
+
ticket,
|
|
85
|
+
target,
|
|
86
|
+
ok,
|
|
87
|
+
cases,
|
|
88
|
+
}: {
|
|
89
|
+
/** `null` for the final review. */
|
|
90
|
+
ticket: number | null
|
|
91
|
+
target: 'ticket' | 'run_branch'
|
|
92
|
+
ok: boolean
|
|
93
|
+
/** The test cases a ticket's gates journal (#581). Defaults to none. */
|
|
94
|
+
cases?: TestCase[]
|
|
95
|
+
}): JournalEntry => ({
|
|
96
|
+
kind: 'gates_run',
|
|
97
|
+
ticket,
|
|
98
|
+
role: null,
|
|
99
|
+
content: {
|
|
100
|
+
target,
|
|
101
|
+
ok,
|
|
102
|
+
...(cases === undefined ? {} : { cases }),
|
|
103
|
+
checks: [
|
|
104
|
+
{
|
|
105
|
+
name: 'test',
|
|
106
|
+
command: 'bun test',
|
|
107
|
+
ok,
|
|
108
|
+
exit_code: ok ? 0 : 1,
|
|
109
|
+
output: ok ? '' : '1 fail',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The engine threw away a ticket worktree's uncommitted changes. With a
|
|
117
|
+
* `bad_test`, the engine found that test checks nothing (#581).
|
|
118
|
+
*/
|
|
119
|
+
export const worktreeReset = ({
|
|
120
|
+
ticket,
|
|
121
|
+
bad_test,
|
|
122
|
+
}: {
|
|
123
|
+
ticket: number
|
|
124
|
+
bad_test?: BadTest
|
|
125
|
+
}): JournalEntry => ({
|
|
126
|
+
kind: 'worktree_reset',
|
|
127
|
+
ticket,
|
|
128
|
+
role: null,
|
|
129
|
+
content: {
|
|
130
|
+
sha: 'red-sha',
|
|
131
|
+
...(bad_test === undefined ? {} : { bad_test }),
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
export const joined = ({ ticket }: { ticket: number }): JournalEntry => ({
|
|
136
|
+
kind: 'ticket_joined',
|
|
137
|
+
ticket,
|
|
138
|
+
role: null,
|
|
139
|
+
content: { ok: true, shas: ['r1', 'g1'] },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
/** A join whose cherry-pick clashed with the run branch (git aborted it). */
|
|
143
|
+
export const joinClashed = ({
|
|
144
|
+
ticket,
|
|
145
|
+
error,
|
|
146
|
+
}: {
|
|
147
|
+
ticket: number
|
|
148
|
+
/** Defaults to a conflict in `src/index.ts`. */
|
|
149
|
+
error?: string
|
|
150
|
+
}): JournalEntry => ({
|
|
151
|
+
kind: 'ticket_joined',
|
|
152
|
+
ticket,
|
|
153
|
+
role: null,
|
|
154
|
+
content: { ok: false, error: error ?? 'CONFLICT (content): src/index.ts' },
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
/** The engine put a ticket's change back on top of the run branch. */
|
|
158
|
+
export const ticketRebased = ({
|
|
159
|
+
ticket,
|
|
160
|
+
cause,
|
|
161
|
+
tests,
|
|
162
|
+
code,
|
|
163
|
+
undone,
|
|
164
|
+
}: {
|
|
165
|
+
ticket: number
|
|
166
|
+
cause: 'clash' | 'join_gates'
|
|
167
|
+
/** The test files that clashed. Defaults to none. */
|
|
168
|
+
tests?: string[]
|
|
169
|
+
/** The code files that clashed. Defaults to none. */
|
|
170
|
+
code?: string[]
|
|
171
|
+
/** The run branch commits undone first. Defaults to none. */
|
|
172
|
+
undone?: string[]
|
|
173
|
+
}): JournalEntry => ({
|
|
174
|
+
kind: 'ticket_rebased',
|
|
175
|
+
ticket,
|
|
176
|
+
role: null,
|
|
177
|
+
content: {
|
|
178
|
+
cause,
|
|
179
|
+
base_sha: 'onto-sha',
|
|
180
|
+
tests: tests ?? [],
|
|
181
|
+
code: code ?? [],
|
|
182
|
+
undone: undone ?? [],
|
|
183
|
+
},
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* After a rebase, the engine sent a ticket's tests back to its test-writer
|
|
188
|
+
* (#489): #12 "Add product" joined, and `src/index.ts` changed.
|
|
189
|
+
*/
|
|
190
|
+
export const testsSentBack = ({
|
|
191
|
+
ticket,
|
|
192
|
+
round,
|
|
193
|
+
reason,
|
|
194
|
+
}: {
|
|
195
|
+
ticket: number
|
|
196
|
+
/** Defaults to 1. */
|
|
197
|
+
round?: number
|
|
198
|
+
/** The bad test's reason. Defaults to "Wrong sum." */
|
|
199
|
+
reason?: string
|
|
200
|
+
}): JournalEntry => ({
|
|
201
|
+
kind: 'tests_sent_back',
|
|
202
|
+
ticket,
|
|
203
|
+
role: null,
|
|
204
|
+
content: {
|
|
205
|
+
round: round ?? 1,
|
|
206
|
+
bad_test: {
|
|
207
|
+
file: 'src/sum.test.ts',
|
|
208
|
+
name: 'sum adds two numbers',
|
|
209
|
+
reason: reason ?? 'Wrong sum.',
|
|
210
|
+
},
|
|
211
|
+
from_sha: 'b0',
|
|
212
|
+
base_sha: 'onto-sha',
|
|
213
|
+
joined: [{ ticket: 12, title: 'Add product' }],
|
|
214
|
+
files: ['src/index.ts', 'src/product.ts'],
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
export const ticketStuck = ({
|
|
219
|
+
ticket,
|
|
220
|
+
reason,
|
|
221
|
+
detail,
|
|
222
|
+
}: {
|
|
223
|
+
ticket: number
|
|
224
|
+
reason: StuckReason
|
|
225
|
+
/** Defaults to "why". */
|
|
226
|
+
detail?: string
|
|
227
|
+
}): JournalEntry => ({
|
|
228
|
+
kind: 'ticket_stuck',
|
|
229
|
+
ticket,
|
|
230
|
+
role: null,
|
|
231
|
+
content: { reason, detail: detail ?? 'why' },
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
export const pushed = ({
|
|
235
|
+
ticket,
|
|
236
|
+
sha,
|
|
237
|
+
}: {
|
|
238
|
+
/** `null` for the final review's push. */
|
|
239
|
+
ticket: number | null
|
|
240
|
+
/** Defaults to `g1`. */
|
|
241
|
+
sha?: string
|
|
242
|
+
}): JournalEntry => ({
|
|
243
|
+
kind: 'run_branch_pushed',
|
|
244
|
+
ticket,
|
|
245
|
+
role: null,
|
|
246
|
+
content: { branch: RUN_BRANCH, sha: sha ?? 'g1' },
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
/** Every step of one ticket, from its worktree to the push, all passing. */
|
|
250
|
+
export const ticketBuilt = ({ ticket }: { ticket: number }): JournalEntry[] => [
|
|
251
|
+
ticketWorktreeCreated({ ticket }),
|
|
252
|
+
baselineTests({ ticket }),
|
|
253
|
+
testsWritten({ ticket }),
|
|
254
|
+
redCheck({ ticket, ok: true }),
|
|
255
|
+
leftoverScan({ ticket, stage: 'red' }),
|
|
256
|
+
commitMade({ ticket, stage: 'red' }),
|
|
257
|
+
implemented({ ticket }),
|
|
258
|
+
gatesRun({ ticket, target: 'ticket', ok: true }),
|
|
259
|
+
leftoverScan({ ticket, stage: 'green' }),
|
|
260
|
+
commitMade({ ticket, stage: 'green' }),
|
|
261
|
+
reviewed({ ticket }),
|
|
262
|
+
joined({ ticket }),
|
|
263
|
+
gatesRun({ ticket, target: 'run_branch', ok: true }),
|
|
264
|
+
pushed({ ticket }),
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
/** One ticket's steps up to and including its approving review. */
|
|
268
|
+
export const ticketApproved = ({
|
|
269
|
+
ticket,
|
|
270
|
+
}: {
|
|
271
|
+
ticket: number
|
|
272
|
+
}): JournalEntry[] => ticketBuilt({ ticket }).slice(0, 11)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { ticketIssue } from './intake-fixtures'
|
|
2
|
+
import {
|
|
3
|
+
journalRecords,
|
|
4
|
+
waitUntil,
|
|
5
|
+
type ManyTicketsScenario,
|
|
6
|
+
} from './many-tickets'
|
|
7
|
+
import {
|
|
8
|
+
APPROVE,
|
|
9
|
+
IMPLEMENTER_RESULT,
|
|
10
|
+
PRACTICE_SPEC_NUMBER,
|
|
11
|
+
practiceSpec,
|
|
12
|
+
SUM,
|
|
13
|
+
SUM_TEST,
|
|
14
|
+
TEST_WRITER_RESULT,
|
|
15
|
+
} from './practice-repo'
|
|
16
|
+
import { AVERAGE, AVERAGE_TEST } from './practice-run'
|
|
17
|
+
|
|
18
|
+
import type { ScriptedTurn } from '../agents/scripted-launcher'
|
|
19
|
+
import { createInMemoryTracker } from '../tracker/in-memory-tracker'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A many-ticket run with all three waits between steps (#559): #21 "Add
|
|
23
|
+
* sum" and #22 "Add product" start from the same run-branch commit, so one
|
|
24
|
+
* waits for the other's baseline tests. #21's reviewer approves once #22's
|
|
25
|
+
* reviewer has started, and #22's reviewer approves once #21 has joined,
|
|
26
|
+
* so #22 is approved while #21's checks run on the run branch: it waits to
|
|
27
|
+
* join after #21. #23 "Add average" waits on #21; once #21 is pushed, #22
|
|
28
|
+
* is still in the join queue, so #23 starts once #22 has joined. No ticket
|
|
29
|
+
* touches another's files, so no join clashes.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const PRODUCT_TEST = `import { describe, expect, test } from 'bun:test'
|
|
33
|
+
|
|
34
|
+
import { product } from './product'
|
|
35
|
+
|
|
36
|
+
describe('product', () => {
|
|
37
|
+
test('multiplies two numbers', () => {
|
|
38
|
+
expect(product({ numbers: [2, 3] })).toBe(6)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('of no numbers is one', () => {
|
|
42
|
+
expect(product({ numbers: [] })).toBe(1)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
const PRODUCT = `export const product = ({ numbers }: { numbers: number[] }): number =>
|
|
48
|
+
numbers.reduce((total, each) => total * each, 1)
|
|
49
|
+
`
|
|
50
|
+
|
|
51
|
+
/** A test-writer's result mapping AC1 and AC2 to the tests of `file`. */
|
|
52
|
+
const mapped = ({
|
|
53
|
+
file,
|
|
54
|
+
names,
|
|
55
|
+
}: {
|
|
56
|
+
file: string
|
|
57
|
+
names: [string, string]
|
|
58
|
+
}) => ({
|
|
59
|
+
outcome: 'tests_written',
|
|
60
|
+
criteria: names.map((name, index) => ({
|
|
61
|
+
criterion_id: `AC${index + 1}`,
|
|
62
|
+
tests: [{ file, name }],
|
|
63
|
+
})),
|
|
64
|
+
summary: 'One test per criterion.',
|
|
65
|
+
assumptions: [],
|
|
66
|
+
run_notes: [],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
/** The baseline, join and start waits, in one run of #21, #22 and #23. */
|
|
70
|
+
export const THREE_WAITS: ManyTicketsScenario = {
|
|
71
|
+
tracker: () =>
|
|
72
|
+
createInMemoryTracker({
|
|
73
|
+
issues: [
|
|
74
|
+
practiceSpec(),
|
|
75
|
+
ticketIssue({
|
|
76
|
+
number: 21,
|
|
77
|
+
title: 'Add sum',
|
|
78
|
+
criteria: [
|
|
79
|
+
'sum adds two numbers',
|
|
80
|
+
'sum of no numbers is zero',
|
|
81
|
+
],
|
|
82
|
+
}),
|
|
83
|
+
ticketIssue({
|
|
84
|
+
number: 22,
|
|
85
|
+
title: 'Add product',
|
|
86
|
+
criteria: [
|
|
87
|
+
'product multiplies two numbers',
|
|
88
|
+
'product of no numbers is one',
|
|
89
|
+
],
|
|
90
|
+
}),
|
|
91
|
+
ticketIssue({
|
|
92
|
+
number: 23,
|
|
93
|
+
title: 'Add average',
|
|
94
|
+
criteria: [
|
|
95
|
+
'average of two numbers is the one between them',
|
|
96
|
+
'average of no numbers is zero',
|
|
97
|
+
],
|
|
98
|
+
blocked_by_section: '- #21',
|
|
99
|
+
blocked_by: [21],
|
|
100
|
+
}),
|
|
101
|
+
],
|
|
102
|
+
sub_tickets: { [PRACTICE_SPEC_NUMBER]: [21, 22, 23] },
|
|
103
|
+
}),
|
|
104
|
+
turns: ({ journal_file }): ScriptedTurn[] => {
|
|
105
|
+
const reviewing = new Set<number>()
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
role: 'test-writer',
|
|
109
|
+
ticket: 21,
|
|
110
|
+
files: { 'src/sum.test.ts': SUM_TEST },
|
|
111
|
+
result: TEST_WRITER_RESULT,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
role: 'test-writer',
|
|
115
|
+
ticket: 22,
|
|
116
|
+
files: { 'src/product.test.ts': PRODUCT_TEST },
|
|
117
|
+
result: mapped({
|
|
118
|
+
file: 'src/product.test.ts',
|
|
119
|
+
names: [
|
|
120
|
+
'product > multiplies two numbers',
|
|
121
|
+
'product > of no numbers is one',
|
|
122
|
+
],
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
role: 'implementer',
|
|
127
|
+
ticket: 21,
|
|
128
|
+
files: { 'src/sum.ts': SUM },
|
|
129
|
+
result: IMPLEMENTER_RESULT,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
role: 'implementer',
|
|
133
|
+
ticket: 22,
|
|
134
|
+
files: { 'src/product.ts': PRODUCT },
|
|
135
|
+
result: { ...IMPLEMENTER_RESULT, summary: 'Added product.' },
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
role: 'ticket-reviewer',
|
|
139
|
+
ticket: 21,
|
|
140
|
+
act: async () =>
|
|
141
|
+
waitUntil({
|
|
142
|
+
check: () => reviewing.has(22),
|
|
143
|
+
what: "#22's reviewer to start",
|
|
144
|
+
}),
|
|
145
|
+
result: APPROVE,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
role: 'ticket-reviewer',
|
|
149
|
+
ticket: 22,
|
|
150
|
+
act: async () => {
|
|
151
|
+
reviewing.add(22)
|
|
152
|
+
await waitUntil({
|
|
153
|
+
check: () =>
|
|
154
|
+
journalRecords(journal_file).some(
|
|
155
|
+
(record) =>
|
|
156
|
+
record.kind === 'ticket_joined' &&
|
|
157
|
+
record.ticket === 21 &&
|
|
158
|
+
record.content.ok
|
|
159
|
+
),
|
|
160
|
+
what: "#21's join",
|
|
161
|
+
})
|
|
162
|
+
},
|
|
163
|
+
result: APPROVE,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
role: 'test-writer',
|
|
167
|
+
ticket: 23,
|
|
168
|
+
files: { 'src/average.test.ts': AVERAGE_TEST },
|
|
169
|
+
result: mapped({
|
|
170
|
+
file: 'src/average.test.ts',
|
|
171
|
+
names: [
|
|
172
|
+
'average > of two numbers is the one between them',
|
|
173
|
+
'average > of no numbers is zero',
|
|
174
|
+
],
|
|
175
|
+
}),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
role: 'implementer',
|
|
179
|
+
ticket: 23,
|
|
180
|
+
files: { 'src/average.ts': AVERAGE },
|
|
181
|
+
result: { ...IMPLEMENTER_RESULT, summary: 'Added average.' },
|
|
182
|
+
},
|
|
183
|
+
{ role: 'ticket-reviewer', ticket: 23, result: APPROVE },
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
}
|