@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,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A final review round as replay rebuilds it: lens results with namespaced
|
|
3
|
+
* finding ids, the round settling once every due lens is in, and the
|
|
4
|
+
* fixers' answers to its fix round.
|
|
5
|
+
*/
|
|
6
|
+
import omit from 'lodash/omit'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
badTestOf,
|
|
10
|
+
declinedBy,
|
|
11
|
+
fixerAnswered,
|
|
12
|
+
handoffAnswered,
|
|
13
|
+
handoffRole,
|
|
14
|
+
type FinishedContent,
|
|
15
|
+
} from './agent-turn-replay'
|
|
16
|
+
import type { FinalFinding, FinalReviewState } from './final-review-state'
|
|
17
|
+
import { setupChangeOf } from './ticket-result-replay'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
isBlocking,
|
|
21
|
+
lensOf,
|
|
22
|
+
type Finding,
|
|
23
|
+
type LensName,
|
|
24
|
+
type LensReviewResult,
|
|
25
|
+
} from '../agents/role-results'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A lens's finding id, namespaced by its lens (`<lens>-<id>`), since lenses
|
|
29
|
+
* choose their ids on their own. An id that already starts with the lens's
|
|
30
|
+
* prefix is kept, so a re-review listing a finding again keeps its id.
|
|
31
|
+
*/
|
|
32
|
+
export const namespacedId = ({
|
|
33
|
+
lens,
|
|
34
|
+
id,
|
|
35
|
+
}: {
|
|
36
|
+
lens: LensName
|
|
37
|
+
id: string
|
|
38
|
+
}): string => (id.startsWith(`${lens}-`) ? id : `${lens}-${id}`)
|
|
39
|
+
|
|
40
|
+
/** A lens's result with every finding and ruling id namespaced. */
|
|
41
|
+
const namespaced = ({
|
|
42
|
+
lens,
|
|
43
|
+
result,
|
|
44
|
+
}: {
|
|
45
|
+
lens: LensName
|
|
46
|
+
result: LensReviewResult
|
|
47
|
+
}): LensReviewResult => ({
|
|
48
|
+
...result,
|
|
49
|
+
findings: result.findings.map((entry) => ({
|
|
50
|
+
...entry,
|
|
51
|
+
id: namespacedId({ lens, id: entry.id }),
|
|
52
|
+
})),
|
|
53
|
+
rulings: result.rulings.map((entry) => ({
|
|
54
|
+
...entry,
|
|
55
|
+
finding_id: namespacedId({ lens, id: entry.finding_id }),
|
|
56
|
+
})),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A fresh-pass finding's id (#548). The fresh lenses see no earlier
|
|
61
|
+
* findings, so an id they choose may already name a different earlier nit
|
|
62
|
+
* or declined finding: such an id gets `fresh-` after its lens prefix. The
|
|
63
|
+
* same nit found again keeps its id, so the PR lists it once.
|
|
64
|
+
*/
|
|
65
|
+
const freshId = ({
|
|
66
|
+
lens,
|
|
67
|
+
finding,
|
|
68
|
+
earlier,
|
|
69
|
+
}: {
|
|
70
|
+
lens: LensName
|
|
71
|
+
finding: Finding
|
|
72
|
+
earlier: Finding[]
|
|
73
|
+
}): string => {
|
|
74
|
+
const clash = earlier.find(({ id }) => id === finding.id)
|
|
75
|
+
if (clash === undefined) return finding.id
|
|
76
|
+
const sameNit =
|
|
77
|
+
!isBlocking(clash) &&
|
|
78
|
+
!isBlocking(finding) &&
|
|
79
|
+
clash.title === finding.title &&
|
|
80
|
+
clash.file === finding.file
|
|
81
|
+
return sameNit
|
|
82
|
+
? finding.id
|
|
83
|
+
: `${lens}-fresh-${finding.id.slice(lens.length + 1)}`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A lens's result, namespaced, with its finding ids kept apart from the
|
|
88
|
+
* earlier rounds' nits and declined findings if this round is the fresh
|
|
89
|
+
* pass (see `freshId`).
|
|
90
|
+
*/
|
|
91
|
+
const lensResultOf = ({
|
|
92
|
+
review,
|
|
93
|
+
lens,
|
|
94
|
+
result,
|
|
95
|
+
}: {
|
|
96
|
+
review: FinalReviewState
|
|
97
|
+
lens: LensName
|
|
98
|
+
result: LensReviewResult
|
|
99
|
+
}): LensReviewResult => {
|
|
100
|
+
const named = namespaced({ lens, result })
|
|
101
|
+
if (!review.fresh) return named
|
|
102
|
+
const earlier = [
|
|
103
|
+
...review.nits,
|
|
104
|
+
...review.declined.map(({ finding }) => finding),
|
|
105
|
+
]
|
|
106
|
+
return {
|
|
107
|
+
...named,
|
|
108
|
+
findings: named.findings.map((finding) => ({
|
|
109
|
+
...finding,
|
|
110
|
+
id: freshId({ lens, finding, earlier }),
|
|
111
|
+
})),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Every due lens finished this round, and its slow tests ran if they were
|
|
117
|
+
* due (#563): the round can settle.
|
|
118
|
+
*/
|
|
119
|
+
export const roundDone = ({ review }: { review: FinalReviewState }): boolean =>
|
|
120
|
+
review.lenses_due.every((lens) => review.results[lens] !== undefined) &&
|
|
121
|
+
(!review.slow_due || review.slow_tests !== null)
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The final review once every due lens finished its round (and its slow
|
|
125
|
+
* tests ran, if due): settles the last fix round's "won't fix" answers per
|
|
126
|
+
* lens (see `declinedBy`), keeps the nits, and opens a fix round on the
|
|
127
|
+
* blocking findings of every due lens, with its fixers, gates, commit, and
|
|
128
|
+
* push afresh (and fresh fixer sessions). Failed slow tests open one too,
|
|
129
|
+
* even with no blocking finding (#563): its findings are empty, so only its
|
|
130
|
+
* gates (and their fix loop) run. No blocking finding and no failed slow
|
|
131
|
+
* test leaves `fix` null: the review is clean.
|
|
132
|
+
*/
|
|
133
|
+
export const settleRound = ({
|
|
134
|
+
review,
|
|
135
|
+
}: {
|
|
136
|
+
review: FinalReviewState
|
|
137
|
+
}): FinalReviewState => {
|
|
138
|
+
const due = review.lenses_due.flatMap((lens) => {
|
|
139
|
+
const result = review.results[lens]
|
|
140
|
+
return result === undefined ? [] : [{ lens, result }]
|
|
141
|
+
})
|
|
142
|
+
const tagged = (lens: LensName, findings: Finding[]): FinalFinding[] =>
|
|
143
|
+
findings.map((entry) => ({ ...entry, lens }))
|
|
144
|
+
const declined = due.flatMap(({ lens, result }) =>
|
|
145
|
+
declinedBy({
|
|
146
|
+
fix:
|
|
147
|
+
review.fix === null
|
|
148
|
+
? null
|
|
149
|
+
: {
|
|
150
|
+
...review.fix,
|
|
151
|
+
findings: review.fix.findings.filter(
|
|
152
|
+
(entry) => entry.lens === lens
|
|
153
|
+
),
|
|
154
|
+
},
|
|
155
|
+
review: result,
|
|
156
|
+
}).map((entry) => ({ ...entry, lens }))
|
|
157
|
+
)
|
|
158
|
+
const nits = due.flatMap(({ lens, result }) =>
|
|
159
|
+
tagged(
|
|
160
|
+
lens,
|
|
161
|
+
result.findings.filter((entry) => !isBlocking(entry))
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
const blocking = due.flatMap(({ lens, result }) =>
|
|
165
|
+
tagged(lens, result.findings.filter(isBlocking))
|
|
166
|
+
)
|
|
167
|
+
const settled: FinalReviewState = {
|
|
168
|
+
...review,
|
|
169
|
+
declined: [...review.declined, ...declined],
|
|
170
|
+
nits: [
|
|
171
|
+
...review.nits,
|
|
172
|
+
...nits.filter(
|
|
173
|
+
(entry, index) =>
|
|
174
|
+
!review.nits.some(({ id }) => id === entry.id) &&
|
|
175
|
+
nits.findIndex(({ id }) => id === entry.id) === index
|
|
176
|
+
),
|
|
177
|
+
],
|
|
178
|
+
}
|
|
179
|
+
const slowFailed = review.slow_due && review.slow_tests?.ok === false
|
|
180
|
+
if (blocking.length === 0 && !slowFailed) return { ...settled, fix: null }
|
|
181
|
+
return {
|
|
182
|
+
...settled,
|
|
183
|
+
fix: {
|
|
184
|
+
round: review.round,
|
|
185
|
+
findings: blocking,
|
|
186
|
+
tests_answered: !blocking.some(({ kind }) => kind === 'test'),
|
|
187
|
+
code_answered: !blocking.some(({ kind }) => kind === 'code'),
|
|
188
|
+
responses: [],
|
|
189
|
+
handed_off: {},
|
|
190
|
+
bad_test: null,
|
|
191
|
+
},
|
|
192
|
+
fixing_started: false,
|
|
193
|
+
sessions: omit(review.sessions, ['test-writer', 'implementer']),
|
|
194
|
+
gates: null,
|
|
195
|
+
gate_fix_rounds: 0,
|
|
196
|
+
leftovers: null,
|
|
197
|
+
commit: null,
|
|
198
|
+
pushed: null,
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Where a finished agent's result goes in the final review: a lens's
|
|
204
|
+
* result this round (settling the round once every due lens finished), or
|
|
205
|
+
* a fixer's answer to the open fix round. An implementer result after
|
|
206
|
+
* failed gates answers a gate fix round, like a ticket's.
|
|
207
|
+
*/
|
|
208
|
+
export const finalResultChange = ({
|
|
209
|
+
review,
|
|
210
|
+
finished,
|
|
211
|
+
}: {
|
|
212
|
+
review: FinalReviewState
|
|
213
|
+
finished: FinishedContent
|
|
214
|
+
}): FinalReviewState => {
|
|
215
|
+
const lens = lensOf({ role: finished.role })
|
|
216
|
+
const { fix } = review
|
|
217
|
+
if (lens !== null && 'verdict' in finished.result) {
|
|
218
|
+
if (!review.lenses_due.includes(lens)) return review
|
|
219
|
+
// The integration lens's first round gives the PR's Summary and
|
|
220
|
+
// Merge Danger; a re-review's nulls keep them (#513).
|
|
221
|
+
const { summary_picture, merge_danger } =
|
|
222
|
+
finished.role === 'integration-lens'
|
|
223
|
+
? finished.result
|
|
224
|
+
: { summary_picture: null, merge_danger: null }
|
|
225
|
+
const next: FinalReviewState = {
|
|
226
|
+
...review,
|
|
227
|
+
summary_picture: summary_picture ?? review.summary_picture,
|
|
228
|
+
merge_danger: merge_danger ?? review.merge_danger,
|
|
229
|
+
results: {
|
|
230
|
+
...review.results,
|
|
231
|
+
[lens]: lensResultOf({
|
|
232
|
+
review,
|
|
233
|
+
lens,
|
|
234
|
+
result: finished.result,
|
|
235
|
+
}),
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
return roundDone({ review: next })
|
|
239
|
+
? settleRound({ review: next })
|
|
240
|
+
: next
|
|
241
|
+
}
|
|
242
|
+
// A fixer asking for a change only the owner may make sticks the final
|
|
243
|
+
// review on it (#617), like a ticket.
|
|
244
|
+
const setup_change = setupChangeOf({ finished })
|
|
245
|
+
if (finished.role === 'test-writer') {
|
|
246
|
+
if (fix === null) return review
|
|
247
|
+
const responses = finished.result.finding_responses
|
|
248
|
+
if (!fix.tests_answered) {
|
|
249
|
+
return {
|
|
250
|
+
...review,
|
|
251
|
+
setup_change,
|
|
252
|
+
fix: {
|
|
253
|
+
...fix,
|
|
254
|
+
tests_answered: true,
|
|
255
|
+
...fixerAnswered({ fix, role: 'test-writer', responses }),
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (handoffRole({ fix }) !== 'test-writer') return review
|
|
260
|
+
return {
|
|
261
|
+
...review,
|
|
262
|
+
setup_change,
|
|
263
|
+
fix: {
|
|
264
|
+
...fix,
|
|
265
|
+
...handoffAnswered({ fix, role: 'test-writer', responses }),
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (finished.role === 'implementer') {
|
|
270
|
+
const { result } = finished
|
|
271
|
+
const responses = result.finding_responses
|
|
272
|
+
if (fix !== null && fix.tests_answered && !fix.code_answered) {
|
|
273
|
+
return {
|
|
274
|
+
...review,
|
|
275
|
+
setup_change,
|
|
276
|
+
fix: {
|
|
277
|
+
...fix,
|
|
278
|
+
code_answered: true,
|
|
279
|
+
...fixerAnswered({ fix, role: 'implementer', responses }),
|
|
280
|
+
bad_test: badTestOf(result),
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (fix !== null && handoffRole({ fix }) === 'implementer') {
|
|
285
|
+
return {
|
|
286
|
+
...review,
|
|
287
|
+
setup_change,
|
|
288
|
+
fix: {
|
|
289
|
+
...fix,
|
|
290
|
+
...handoffAnswered({ fix, role: 'implementer', responses }),
|
|
291
|
+
bad_test: badTestOf(result),
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (review.gates === null) return review
|
|
296
|
+
// An `environment` answer to failed gates reruns them, using no
|
|
297
|
+
// gate fix round (#613).
|
|
298
|
+
if (result.outcome === 'environment' && !review.gates.ok) {
|
|
299
|
+
return {
|
|
300
|
+
...review,
|
|
301
|
+
setup_change,
|
|
302
|
+
gates: null,
|
|
303
|
+
environment_reruns: review.environment_reruns + 1,
|
|
304
|
+
environment: {
|
|
305
|
+
role: 'implementer',
|
|
306
|
+
evidence: result.environment?.evidence ?? '',
|
|
307
|
+
check: 'gates',
|
|
308
|
+
failed: review.gates,
|
|
309
|
+
},
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
...review,
|
|
314
|
+
setup_change,
|
|
315
|
+
gates: null,
|
|
316
|
+
gate_fix_rounds: review.gate_fix_rounds + 1,
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return review
|
|
320
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of the final review as replay rebuilds it, with its findings,
|
|
3
|
+
* fix round and failures, and the final review before it starts.
|
|
4
|
+
*/
|
|
5
|
+
import type { AgentFailure, StuckReason } from './journal-record'
|
|
6
|
+
import type {
|
|
7
|
+
DeclinedFinding,
|
|
8
|
+
EnvironmentRerun,
|
|
9
|
+
ReplayedGates,
|
|
10
|
+
ReviewFix,
|
|
11
|
+
TicketProgress,
|
|
12
|
+
} from './ticket-progress'
|
|
13
|
+
|
|
14
|
+
import type {
|
|
15
|
+
AgentRole,
|
|
16
|
+
Finding,
|
|
17
|
+
LensName,
|
|
18
|
+
LensReviewResult,
|
|
19
|
+
MergeDanger,
|
|
20
|
+
} from '../agents/role-results'
|
|
21
|
+
import type { LeftoverHit } from '../gates/gate-schemas'
|
|
22
|
+
|
|
23
|
+
/** A final review finding: its id namespaced as `<lens>-<id>`, and its lens. */
|
|
24
|
+
export type FinalFinding = Finding & { lens: LensName }
|
|
25
|
+
|
|
26
|
+
/** A finding declined in the final review, with the lens that let it go. */
|
|
27
|
+
export type FinalDeclined = DeclinedFinding & { lens: LensName }
|
|
28
|
+
|
|
29
|
+
/** A rule file for the rules lens, as the engine read it (`null`: unreadable). */
|
|
30
|
+
export type RuleFile = { path: string; text: string | null }
|
|
31
|
+
|
|
32
|
+
/** A failed agent turn no agent of that role finished after. */
|
|
33
|
+
export type ReplayedAgentFailure = {
|
|
34
|
+
role: AgentRole
|
|
35
|
+
error: string
|
|
36
|
+
failure: AgentFailure
|
|
37
|
+
session_id: string | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The open final review fix round: the blocking findings of every lens due
|
|
42
|
+
* in the round, and which fixers answered. Like a ticket's `ReviewFix`.
|
|
43
|
+
*/
|
|
44
|
+
export type FinalReviewFix = Omit<ReviewFix, 'findings'> & {
|
|
45
|
+
findings: FinalFinding[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Where the final review stands, from its records: its round, the lenses
|
|
50
|
+
* due, each lens's result this round, the open fix round and its fixers,
|
|
51
|
+
* gates, commit, and push, and what goes in the PR. Ticket-less agent,
|
|
52
|
+
* gate, scan, commit, and push records belong here once
|
|
53
|
+
* `final_review_started` is journaled.
|
|
54
|
+
*/
|
|
55
|
+
export type FinalReviewState = {
|
|
56
|
+
/** 0 before the final review starts, then 1, 2, ... */
|
|
57
|
+
round: number
|
|
58
|
+
from_sha: string | null
|
|
59
|
+
head_sha: string | null
|
|
60
|
+
/** The files `git diff <from_sha>..<head_sha>` changes. */
|
|
61
|
+
files: string[]
|
|
62
|
+
/** The lenses due this round. */
|
|
63
|
+
lenses_due: LensName[]
|
|
64
|
+
rules: RuleFile[]
|
|
65
|
+
/** Each due lens's result this round, finding ids namespaced. */
|
|
66
|
+
results: Partial<Record<LensName, LensReviewResult>>
|
|
67
|
+
/** The open fix round; `null` before the first review settles or once clean. */
|
|
68
|
+
fix: FinalReviewFix | null
|
|
69
|
+
/** `final_review_fixing` is journaled for the open fix round. */
|
|
70
|
+
fixing_started: boolean
|
|
71
|
+
/** The session of each role's latest turn, for follow-ups. */
|
|
72
|
+
sessions: Partial<Record<AgentRole, string>>
|
|
73
|
+
/** Each role's failed turn, if no agent of that role finished after it. */
|
|
74
|
+
agent_failures: Partial<Record<AgentRole, ReplayedAgentFailure>>
|
|
75
|
+
/** Failed tries (agent, result, guard) per role, over the whole final review. */
|
|
76
|
+
failed_tries: Partial<Record<AgentRole, number>>
|
|
77
|
+
/** Engine failures in a row, per role. */
|
|
78
|
+
engine_failures: Partial<Record<AgentRole, number>>
|
|
79
|
+
/** The open fix round's gates, and its gate fix follow-ups. */
|
|
80
|
+
gates: ReplayedGates | null
|
|
81
|
+
gate_fix_rounds: number
|
|
82
|
+
/**
|
|
83
|
+
* The fixer's `environment` answers to failed gates (#613), over the
|
|
84
|
+
* whole final review, each a rerun that used no gate fix round.
|
|
85
|
+
*/
|
|
86
|
+
environment_reruns: number
|
|
87
|
+
/** The latest `environment` answer, until the next agent answers. */
|
|
88
|
+
environment: EnvironmentRerun | null
|
|
89
|
+
/** The open fix round's leftover scan and commit. */
|
|
90
|
+
leftovers: LeftoverHit[] | null
|
|
91
|
+
commit: { sha: string; files: string[] } | null
|
|
92
|
+
/** The open fix round's push. */
|
|
93
|
+
pushed: string | null
|
|
94
|
+
/** This round's slow tests are due (#563): round 1, with a slow command. */
|
|
95
|
+
slow_due: boolean
|
|
96
|
+
/** A fix commit was pushed since round 1: a fresh pass is due once clean (#548). */
|
|
97
|
+
fix_pushed: boolean
|
|
98
|
+
/** This round is the fresh pass (#548), or its fix round. */
|
|
99
|
+
fresh: boolean
|
|
100
|
+
/** The fresh pass started; there is at most one per final review (#548). */
|
|
101
|
+
fresh_started: boolean
|
|
102
|
+
/** This round's slow test run (`gates_run` `final_slow`), once journaled. */
|
|
103
|
+
slow_tests: ReplayedGates | null
|
|
104
|
+
/** Every nit the lenses reported, one per id. */
|
|
105
|
+
nits: FinalFinding[]
|
|
106
|
+
declined: FinalDeclined[]
|
|
107
|
+
/** Every assumption a final review agent made, oldest first. */
|
|
108
|
+
assumptions: string[]
|
|
109
|
+
/**
|
|
110
|
+
* How many turns each final review role ran out of turns (#543), in the
|
|
111
|
+
* order each first did, for the PR.
|
|
112
|
+
*/
|
|
113
|
+
out_of_turns: Partial<Record<AgentRole, number>>
|
|
114
|
+
passed: boolean
|
|
115
|
+
stuck: { reason: StuckReason; detail: string } | null
|
|
116
|
+
shipped: boolean
|
|
117
|
+
/**
|
|
118
|
+
* The commit of the edits left uncommitted at ship time (#509), not
|
|
119
|
+
* reviewed; `null` with none.
|
|
120
|
+
*/
|
|
121
|
+
shipped_edits: { sha: string; message: string; files: string[] } | null
|
|
122
|
+
/**
|
|
123
|
+
* A change only the owner may make, asked for by a fixer (#617); the
|
|
124
|
+
* final review is stuck on it until a `retry`.
|
|
125
|
+
*/
|
|
126
|
+
setup_change: TicketProgress['setup_change']
|
|
127
|
+
/** The fixer whose turn a crash cut off; cleared once a fixer starts. */
|
|
128
|
+
crashed_turn: AgentRole | null
|
|
129
|
+
/**
|
|
130
|
+
* The PR's Summary picture, from the integration lens (#513). It is kept
|
|
131
|
+
* across rounds; a later non-null answer replaces it.
|
|
132
|
+
*/
|
|
133
|
+
summary_picture: string | null
|
|
134
|
+
/**
|
|
135
|
+
* The PR's Merge Danger, from the integration lens (#513). It is kept
|
|
136
|
+
* across rounds; a later non-null answer replaces it.
|
|
137
|
+
*/
|
|
138
|
+
merge_danger: MergeDanger | null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The final review before it starts. */
|
|
142
|
+
export const EMPTY_FINAL_REVIEW: FinalReviewState = {
|
|
143
|
+
round: 0,
|
|
144
|
+
from_sha: null,
|
|
145
|
+
head_sha: null,
|
|
146
|
+
files: [],
|
|
147
|
+
lenses_due: [],
|
|
148
|
+
rules: [],
|
|
149
|
+
results: {},
|
|
150
|
+
fix: null,
|
|
151
|
+
fixing_started: false,
|
|
152
|
+
sessions: {},
|
|
153
|
+
agent_failures: {},
|
|
154
|
+
failed_tries: {},
|
|
155
|
+
engine_failures: {},
|
|
156
|
+
gates: null,
|
|
157
|
+
gate_fix_rounds: 0,
|
|
158
|
+
environment_reruns: 0,
|
|
159
|
+
environment: null,
|
|
160
|
+
leftovers: null,
|
|
161
|
+
commit: null,
|
|
162
|
+
pushed: null,
|
|
163
|
+
slow_due: false,
|
|
164
|
+
fix_pushed: false,
|
|
165
|
+
fresh: false,
|
|
166
|
+
fresh_started: false,
|
|
167
|
+
slow_tests: null,
|
|
168
|
+
nits: [],
|
|
169
|
+
declined: [],
|
|
170
|
+
assumptions: [],
|
|
171
|
+
out_of_turns: {},
|
|
172
|
+
passed: false,
|
|
173
|
+
stuck: null,
|
|
174
|
+
shipped: false,
|
|
175
|
+
shipped_edits: null,
|
|
176
|
+
setup_change: null,
|
|
177
|
+
crashed_turn: null,
|
|
178
|
+
summary_picture: null,
|
|
179
|
+
merge_danger: null,
|
|
180
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { ENTRY_FIELDS } from './entry-fields'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
GateCheckSchema,
|
|
7
|
+
RedCheckResultSchema,
|
|
8
|
+
TestCaseRefSchema,
|
|
9
|
+
TestCaseSchema,
|
|
10
|
+
TestRunSchema,
|
|
11
|
+
} from '../gates/gate-schemas'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The journal entries of the checks on a ticket or the run branch: the red
|
|
15
|
+
* check, the dependency install, and the gates.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A lone rerun (#544): the check ran again while no other test run went,
|
|
20
|
+
* holding every test slot, after its old tests failed in another ticket
|
|
21
|
+
* too. Left out on every other check.
|
|
22
|
+
*/
|
|
23
|
+
const EXCLUSIVE_FIELD = { exclusive: z.boolean().optional() }
|
|
24
|
+
|
|
25
|
+
export const RedCheckEntrySchema = z.object({
|
|
26
|
+
...ENTRY_FIELDS,
|
|
27
|
+
kind: z.literal('red_check'),
|
|
28
|
+
content: RedCheckResultSchema.extend({
|
|
29
|
+
tests: TestRunSchema,
|
|
30
|
+
...EXCLUSIVE_FIELD,
|
|
31
|
+
}),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/** Where the gates or the install ran: a ticket's worktree, or the run branch. */
|
|
35
|
+
export const GateTargetSchema = z.enum(['ticket', 'run_branch'])
|
|
36
|
+
|
|
37
|
+
export type GateTarget = z.infer<typeof GateTargetSchema>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Where a `gates_run` ran: a ticket's worktree, the run branch, or the
|
|
41
|
+
* final review's slow tests on the run branch (#563), which run once
|
|
42
|
+
* round 1's lenses are in and are not the fix round's gates.
|
|
43
|
+
*/
|
|
44
|
+
const GatesRunTargetSchema = z.enum([...GateTargetSchema.options, 'final_slow'])
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The engine installed the dependencies from the lockfile, without changing
|
|
48
|
+
* it, in a new ticket worktree or the run branch's checkout, before any agent
|
|
49
|
+
* or gate ran there. `check` is `null` when there is no `package.json`, so
|
|
50
|
+
* nothing to install. The run branch's record has no ticket.
|
|
51
|
+
*/
|
|
52
|
+
export const DependenciesInstalledEntrySchema = z.object({
|
|
53
|
+
...ENTRY_FIELDS,
|
|
54
|
+
kind: z.literal('dependencies_installed'),
|
|
55
|
+
content: z.object({
|
|
56
|
+
target: GateTargetSchema,
|
|
57
|
+
check: GateCheckSchema.nullable(),
|
|
58
|
+
}),
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export const GatesRunEntrySchema = z.object({
|
|
62
|
+
...ENTRY_FIELDS,
|
|
63
|
+
kind: z.literal('gates_run'),
|
|
64
|
+
content: z.object({
|
|
65
|
+
target: GatesRunTargetSchema,
|
|
66
|
+
ok: z.boolean(),
|
|
67
|
+
checks: z.array(GateCheckSchema),
|
|
68
|
+
/**
|
|
69
|
+
* A ticket's gates only: the test cases of the ticket's red test
|
|
70
|
+
* files, with their `expect()` counts (#581). Missing in old journals.
|
|
71
|
+
*/
|
|
72
|
+
cases: z.array(TestCaseSchema).optional(),
|
|
73
|
+
/**
|
|
74
|
+
* A ticket's and a join's gates only: the test cases that failed in
|
|
75
|
+
* any bun run (#544), so a test failing in two tickets is spotted.
|
|
76
|
+
* Left out with none, and in old journals.
|
|
77
|
+
*/
|
|
78
|
+
failed_cases: z.array(TestCaseRefSchema).optional(),
|
|
79
|
+
...EXCLUSIVE_FIELD,
|
|
80
|
+
}),
|
|
81
|
+
})
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { ENTRY_FIELDS } from './entry-fields'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
IntakeProblemSchema,
|
|
7
|
+
IntakeReadSchema,
|
|
8
|
+
LeftOutTicketSchema,
|
|
9
|
+
SpecSnapshotSchema,
|
|
10
|
+
TicketSnapshotSchema,
|
|
11
|
+
} from '../intake/intake-schemas'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The journal entries of intake: what it read, a refusal, nothing to do,
|
|
15
|
+
* and the spec and ticket snapshots.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const IntakeReadEntrySchema = z.object({
|
|
19
|
+
...ENTRY_FIELDS,
|
|
20
|
+
kind: z.literal('intake_read'),
|
|
21
|
+
content: IntakeReadSchema,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A label the refusal couldn't add or remove (#500), with the tracker's
|
|
26
|
+
* error. The refusal goes on without it.
|
|
27
|
+
*/
|
|
28
|
+
export const LabelFailureSchema = z.object({
|
|
29
|
+
ticket: z.number().int().positive(),
|
|
30
|
+
label: z.string(),
|
|
31
|
+
change: z.enum(['add', 'remove']),
|
|
32
|
+
error: z.string(),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export type LabelFailure = z.infer<typeof LabelFailureSchema>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Intake refused the run. `label_failures` are the labels it couldn't
|
|
39
|
+
* move; older journals have none.
|
|
40
|
+
*/
|
|
41
|
+
export const IntakeRefusedEntrySchema = z.object({
|
|
42
|
+
...ENTRY_FIELDS,
|
|
43
|
+
kind: z.literal('intake_refused'),
|
|
44
|
+
content: z.object({
|
|
45
|
+
problems: z.array(IntakeProblemSchema),
|
|
46
|
+
label_failures: z.array(LabelFailureSchema).default([]),
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The run ends with nothing to do: at intake, as every ticket of the spec
|
|
52
|
+
* is closed, or after it, as every ticket's work was already on the base
|
|
53
|
+
* branch (`already_done`, #484; the engine closed those tickets). At
|
|
54
|
+
* intake, `left_out` names the open tickets left for a person (#499), when
|
|
55
|
+
* those are all that is open. Older journals have none.
|
|
56
|
+
*/
|
|
57
|
+
export const NothingToDoEntrySchema = z.object({
|
|
58
|
+
...ENTRY_FIELDS,
|
|
59
|
+
kind: z.literal('nothing_to_do'),
|
|
60
|
+
content: z.object({
|
|
61
|
+
closed_tickets: z.array(z.number().int()),
|
|
62
|
+
already_done: z.array(z.number().int().positive()).default([]),
|
|
63
|
+
left_out: z.array(LeftOutTicketSchema).default([]),
|
|
64
|
+
}),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
export const SpecSnapshotEntrySchema = z.object({
|
|
68
|
+
...ENTRY_FIELDS,
|
|
69
|
+
kind: z.literal('spec_snapshot'),
|
|
70
|
+
content: z.object({
|
|
71
|
+
spec: SpecSnapshotSchema,
|
|
72
|
+
/** The open tickets in blocker order; one `ticket_snapshot` each. */
|
|
73
|
+
ticket_order: z.array(z.number().int().positive()),
|
|
74
|
+
closed_tickets: z.array(z.number().int().positive()),
|
|
75
|
+
/**
|
|
76
|
+
* The open tickets left out for a person, and why (#499). The run
|
|
77
|
+
* doesn't build them or touch them. Older journals have none.
|
|
78
|
+
*/
|
|
79
|
+
left_out: z.array(LeftOutTicketSchema).default([]),
|
|
80
|
+
}),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export const TicketSnapshotEntrySchema = z.object({
|
|
84
|
+
...ENTRY_FIELDS,
|
|
85
|
+
kind: z.literal('ticket_snapshot'),
|
|
86
|
+
content: TicketSnapshotSchema,
|
|
87
|
+
})
|