@telora/daemon 0.23.16 → 0.23.18

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.
Files changed (165) hide show
  1. package/build-info.json +2 -2
  2. package/dist/ci/candidate-ref.d.ts +13 -0
  3. package/dist/ci/candidate-ref.d.ts.map +1 -1
  4. package/dist/ci/candidate-ref.js +18 -0
  5. package/dist/ci/candidate-ref.js.map +1 -1
  6. package/dist/completion/review-exit/review-census-record.d.ts +16 -2
  7. package/dist/completion/review-exit/review-census-record.d.ts.map +1 -1
  8. package/dist/completion/review-exit/review-census-record.js +23 -2
  9. package/dist/completion/review-exit/review-census-record.js.map +1 -1
  10. package/dist/completion/review-exit/review-exit-phase.d.ts +6 -0
  11. package/dist/completion/review-exit/review-exit-phase.d.ts.map +1 -1
  12. package/dist/completion/review-exit/review-exit-phase.js.map +1 -1
  13. package/dist/completion/state-cascade/settle-injection-sweep.d.ts.map +1 -1
  14. package/dist/completion/state-cascade/settle-injection-sweep.js +2 -1
  15. package/dist/completion/state-cascade/settle-injection-sweep.js.map +1 -1
  16. package/dist/completion/team-outcome/decide-shared.d.ts +3 -1
  17. package/dist/completion/team-outcome/decide-shared.d.ts.map +1 -1
  18. package/dist/completion/team-outcome/decide-shared.js +14 -3
  19. package/dist/completion/team-outcome/decide-shared.js.map +1 -1
  20. package/dist/completion/team-outcome/team-outcome.d.ts +14 -0
  21. package/dist/completion/team-outcome/team-outcome.d.ts.map +1 -1
  22. package/dist/completion/team-outcome/team-outcome.js +18 -0
  23. package/dist/completion/team-outcome/team-outcome.js.map +1 -1
  24. package/dist/composition/execution-merge-context.d.ts +7 -1
  25. package/dist/composition/execution-merge-context.d.ts.map +1 -1
  26. package/dist/composition/execution-merge-context.js +42 -6
  27. package/dist/composition/execution-merge-context.js.map +1 -1
  28. package/dist/composition/execution-recovery-context.d.ts.map +1 -1
  29. package/dist/composition/execution-recovery-context.js +2 -0
  30. package/dist/composition/execution-recovery-context.js.map +1 -1
  31. package/dist/composition/state-cascade-context.d.ts.map +1 -1
  32. package/dist/composition/state-cascade-context.js +8 -3
  33. package/dist/composition/state-cascade-context.js.map +1 -1
  34. package/dist/composition/verification-context.d.ts.map +1 -1
  35. package/dist/composition/verification-context.js +2 -0
  36. package/dist/composition/verification-context.js.map +1 -1
  37. package/dist/daemon-state/focus-team-state.d.ts +2 -0
  38. package/dist/daemon-state/focus-team-state.d.ts.map +1 -1
  39. package/dist/daemon-state/focus-team-state.js +6 -1
  40. package/dist/daemon-state/focus-team-state.js.map +1 -1
  41. package/dist/daemon-state/lead-process-ownership.d.ts.map +1 -1
  42. package/dist/daemon-state/lead-process-ownership.js +1 -0
  43. package/dist/daemon-state/lead-process-ownership.js.map +1 -1
  44. package/dist/daemon-state/provider-window.d.ts.map +1 -1
  45. package/dist/daemon-state/provider-window.js +1 -0
  46. package/dist/daemon-state/provider-window.js.map +1 -1
  47. package/dist/focus/crash-recovery.d.ts +7 -0
  48. package/dist/focus/crash-recovery.d.ts.map +1 -1
  49. package/dist/focus/crash-recovery.js +26 -13
  50. package/dist/focus/crash-recovery.js.map +1 -1
  51. package/dist/git/merge-lock.d.ts.map +1 -1
  52. package/dist/git/merge-lock.js +3 -1
  53. package/dist/git/merge-lock.js.map +1 -1
  54. package/dist/merge/candidate-rework.d.ts +3 -3
  55. package/dist/merge/candidate-rework.d.ts.map +1 -1
  56. package/dist/merge/candidate-rework.js +30 -10
  57. package/dist/merge/candidate-rework.js.map +1 -1
  58. package/dist/merge/completion-hold-record.d.ts +16 -0
  59. package/dist/merge/completion-hold-record.d.ts.map +1 -1
  60. package/dist/merge/completion-hold-record.js +60 -1
  61. package/dist/merge/completion-hold-record.js.map +1 -1
  62. package/dist/merge/completion-judge.d.ts +44 -0
  63. package/dist/merge/completion-judge.d.ts.map +1 -0
  64. package/dist/merge/completion-judge.js +36 -0
  65. package/dist/merge/completion-judge.js.map +1 -0
  66. package/dist/merge/completion-local-judge.d.ts +90 -0
  67. package/dist/merge/completion-local-judge.d.ts.map +1 -0
  68. package/dist/merge/completion-local-judge.js +193 -0
  69. package/dist/merge/completion-local-judge.js.map +1 -0
  70. package/dist/merge/completion-merge-driver.d.ts +31 -7
  71. package/dist/merge/completion-merge-driver.d.ts.map +1 -1
  72. package/dist/merge/completion-merge-driver.js +36 -8
  73. package/dist/merge/completion-merge-driver.js.map +1 -1
  74. package/dist/merge/completion-merge-gate.d.ts +10 -2
  75. package/dist/merge/completion-merge-gate.d.ts.map +1 -1
  76. package/dist/merge/completion-merge-gate.js +65 -19
  77. package/dist/merge/completion-merge-gate.js.map +1 -1
  78. package/dist/merge/merge-back-loop.d.ts +9 -1
  79. package/dist/merge/merge-back-loop.d.ts.map +1 -1
  80. package/dist/merge/merge-back-loop.js +28 -1
  81. package/dist/merge/merge-back-loop.js.map +1 -1
  82. package/dist/nexus/file-escalation.d.ts.map +1 -1
  83. package/dist/nexus/file-escalation.js +1 -0
  84. package/dist/nexus/file-escalation.js.map +1 -1
  85. package/dist/queries/api-client/shared.js +1 -1
  86. package/dist/queries/verification/verification.d.ts +8 -0
  87. package/dist/queries/verification/verification.d.ts.map +1 -1
  88. package/dist/queries/verification/verification.js +11 -1
  89. package/dist/queries/verification/verification.js.map +1 -1
  90. package/dist/spawn/codex-limit.d.ts +20 -4
  91. package/dist/spawn/codex-limit.d.ts.map +1 -1
  92. package/dist/spawn/codex-limit.js +36 -7
  93. package/dist/spawn/codex-limit.js.map +1 -1
  94. package/dist/spawn/helpers.d.ts.map +1 -1
  95. package/dist/spawn/helpers.js +13 -6
  96. package/dist/spawn/helpers.js.map +1 -1
  97. package/dist/spawn/model-reached.d.ts +6 -0
  98. package/dist/spawn/model-reached.d.ts.map +1 -1
  99. package/dist/spawn/model-reached.js +8 -0
  100. package/dist/spawn/model-reached.js.map +1 -1
  101. package/dist/spawn/next-spawn-cooldown.d.ts.map +1 -1
  102. package/dist/spawn/next-spawn-cooldown.js +2 -1
  103. package/dist/spawn/next-spawn-cooldown.js.map +1 -1
  104. package/dist/supervision/spawn-cooldown.d.ts +10 -1
  105. package/dist/supervision/spawn-cooldown.d.ts.map +1 -1
  106. package/dist/supervision/spawn-cooldown.js +3 -2
  107. package/dist/supervision/spawn-cooldown.js.map +1 -1
  108. package/dist/testing/fixtures/api-client.js +1 -1
  109. package/dist/testing/fixtures/codex-stream-session.d.ts.map +1 -1
  110. package/dist/testing/fixtures/codex-stream-session.js +1 -0
  111. package/dist/testing/fixtures/codex-stream-session.js.map +1 -1
  112. package/dist/testing/fixtures/completion-event-deps.js +1 -1
  113. package/dist/testing/fixtures/completion-judge-stubs.d.ts +15 -0
  114. package/dist/testing/fixtures/completion-judge-stubs.d.ts.map +1 -0
  115. package/dist/testing/fixtures/completion-judge-stubs.js +32 -0
  116. package/dist/testing/fixtures/completion-judge-stubs.js.map +1 -0
  117. package/dist/testing/fixtures/exit-capture-verify.d.ts +1 -0
  118. package/dist/testing/fixtures/exit-capture-verify.d.ts.map +1 -1
  119. package/dist/testing/fixtures/exit-capture-verify.js +3 -1
  120. package/dist/testing/fixtures/exit-capture-verify.js.map +1 -1
  121. package/dist/testing/fixtures/index.js +1 -1
  122. package/dist/testing/fixtures/process-close-context.d.ts.map +1 -1
  123. package/dist/testing/fixtures/process-close-context.js +1 -0
  124. package/dist/testing/fixtures/process-close-context.js.map +1 -1
  125. package/dist/testing/fixtures/review-exit-deps.js +1 -1
  126. package/dist/testing/fixtures/verification-deps.d.ts +6 -0
  127. package/dist/testing/fixtures/verification-deps.d.ts.map +1 -1
  128. package/dist/testing/fixtures/verification-deps.js +11 -0
  129. package/dist/testing/fixtures/verification-deps.js.map +1 -1
  130. package/dist/types/focus-team-state.d.ts +16 -2
  131. package/dist/types/focus-team-state.d.ts.map +1 -1
  132. package/dist/types/focus-team-state.js.map +1 -1
  133. package/dist/types/index.js +1 -1
  134. package/dist/verify/deterministic-gate/verification-checkout.d.ts.map +1 -1
  135. package/dist/verify/deterministic-gate/verification-checkout.js +1 -0
  136. package/dist/verify/deterministic-gate/verification-checkout.js.map +1 -1
  137. package/dist/verify/deterministic-gate/verification-deterministic.d.ts.map +1 -1
  138. package/dist/verify/deterministic-gate/verification-deterministic.js +1 -0
  139. package/dist/verify/deterministic-gate/verification-deterministic.js.map +1 -1
  140. package/dist/verify/effect-reconciliation/injection-stamp.d.ts +33 -18
  141. package/dist/verify/effect-reconciliation/injection-stamp.d.ts.map +1 -1
  142. package/dist/verify/effect-reconciliation/injection-stamp.js +154 -82
  143. package/dist/verify/effect-reconciliation/injection-stamp.js.map +1 -1
  144. package/dist/verify/effect-reconciliation/pending-injection-stamp.d.ts +36 -0
  145. package/dist/verify/effect-reconciliation/pending-injection-stamp.d.ts.map +1 -0
  146. package/dist/verify/effect-reconciliation/pending-injection-stamp.js +157 -0
  147. package/dist/verify/effect-reconciliation/pending-injection-stamp.js.map +1 -0
  148. package/dist/verify/effect-reconciliation/verify-restatement.js +1 -1
  149. package/dist/verify/engine/verification-deps.d.ts +10 -1
  150. package/dist/verify/engine/verification-deps.d.ts.map +1 -1
  151. package/dist/verify/engine/verification-deps.js +1 -1
  152. package/dist/verify/engine/verification-engine.js +8 -8
  153. package/dist/verify/engine/verification-engine.js.map +1 -1
  154. package/dist/verify/gate-config/verification-manifest.d.ts +21 -1
  155. package/dist/verify/gate-config/verification-manifest.d.ts.map +1 -1
  156. package/dist/verify/gate-config/verification-manifest.js +25 -1
  157. package/dist/verify/gate-config/verification-manifest.js.map +1 -1
  158. package/dist/verify/run-selection/verification-default-on.d.ts +2 -0
  159. package/dist/verify/run-selection/verification-default-on.d.ts.map +1 -1
  160. package/dist/verify/run-selection/verification-default-on.js +1 -1
  161. package/dist/verify/run-selection/verification-default-on.js.map +1 -1
  162. package/dist/worktree/focus-worktree-state.d.ts.map +1 -1
  163. package/dist/worktree/focus-worktree-state.js +1 -0
  164. package/dist/worktree/focus-worktree-state.js.map +1 -1
  165. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-local-judge.d.ts","sourceRoot":"","sources":["../../src/merge/completion-local-judge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,193 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/merge/completion-local-judge.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * The LOCAL completion judge: the daemon runs a product's completion command on
6
+ * exactly a completed focus's integration candidate (the focus head after the
7
+ * integration tip was merged in) and records the verdict, so integration
8
+ * fast-forwards only to a commit whose combined tree passed the product's full
9
+ * suite -- without depending on a remote CI that may publish nothing
10
+ * (merge/completion-judge.ts; docs/DECISIONS.md, "The completion candidate is
11
+ * judged where the product declares").
12
+ *
13
+ * The run happens in a detached checkout of the focus worktree's HEAD, made the
14
+ * way a deterministic verify makes one (verify/deterministic-gate/verification-checkout.ts):
15
+ * the focus worktree must be provisioned from the candidate's own lockfiles (a
16
+ * merge-out that changed them waits for the install), the checkout must hold
17
+ * the candidate sha (a head that moved is not judged), and a verdict rendered
18
+ * while the linked dependencies moved is discarded. The command runs through the
19
+ * same process-group runner, with the declared timeout.
20
+ *
21
+ * A run is long, so it is STARTED by the merge-back loop and never awaited by
22
+ * it: the verdict lands in a durable record (one file per product, focus and
23
+ * sha under the global state dir), which the completion gate reads on a later
24
+ * tick. One run at a time per daemon -- a full suite is heavy and the host also
25
+ * runs teams. What counts as a verdict is a run that exited (any exit code) or
26
+ * was killed by its timeout; a run that could not start is NOT a verdict -- it is
27
+ * logged and retried after a backoff, never charged to the focus.
28
+ *
29
+ * @module merge/completion-local-judge
30
+ */
31
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
32
+ import { resolve } from 'node:path';
33
+ import { resolveGlobalStateDir } from '../daemon-state/daemon-process.js';
34
+ import { createLogger } from '../log/daemon-log.js';
35
+ const log = createLogger({ module: 'completion-merge' });
36
+ /** How long a run that could not START waits before it is tried again. */
37
+ export const LOCAL_RUN_START_BACKOFF_MS = 10 * 60 * 1000;
38
+ // ── Durable verdict record ───────────────────────────────────────────
39
+ function verdictDir() {
40
+ return resolve(resolveGlobalStateDir(), 'completion-verdicts');
41
+ }
42
+ /** The record file for one candidate; `dir` defaults to the global state dir. Exported for tests. */
43
+ export function localVerdictPath(productId, focusId, sha, dir = verdictDir()) {
44
+ return resolve(dir, `${productId}-${focusId}-${sha}.json`.replace(/[^a-zA-Z0-9._-]/g, '_'));
45
+ }
46
+ /** Write a verdict atomically (temp + rename). Throws when it cannot. */
47
+ export function writeLocalVerdict(verdict, dir = verdictDir()) {
48
+ mkdirSync(dir, { recursive: true });
49
+ const path = localVerdictPath(verdict.productId, verdict.focusId, verdict.sha, dir);
50
+ const tmp = `${path}.${process.pid}.tmp`;
51
+ writeFileSync(tmp, JSON.stringify(verdict));
52
+ renameSync(tmp, path);
53
+ }
54
+ /** The recorded verdict on a candidate, or null when none is recorded (or it is unreadable). */
55
+ export function readLocalVerdict(productId, focusId, sha, dir = verdictDir()) {
56
+ let parsed;
57
+ try {
58
+ parsed = JSON.parse(readFileSync(localVerdictPath(productId, focusId, sha, dir), 'utf8'));
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ if (typeof parsed !== 'object' || parsed === null)
64
+ return null;
65
+ const v = parsed;
66
+ if (v.sha !== sha || (v.outcome !== 'passed' && v.outcome !== 'failed') || typeof v.command !== 'string')
67
+ return null;
68
+ return {
69
+ productId,
70
+ focusId,
71
+ sha,
72
+ outcome: v.outcome,
73
+ command: v.command,
74
+ rationale: typeof v.rationale === 'string' ? v.rationale : '',
75
+ exitCode: typeof v.exitCode === 'number' ? v.exitCode : null,
76
+ timedOut: v.timedOut === true,
77
+ logPath: typeof v.logPath === 'string' ? v.logPath : null,
78
+ at: typeof v.at === 'number' ? v.at : 0,
79
+ };
80
+ }
81
+ // ── In-flight runs ───────────────────────────────────────────────────
82
+ /** The one run in flight (a full suite at a time per daemon). */
83
+ let slot = null;
84
+ /** When a run of (focus, sha) last failed to START, for the backoff. */
85
+ const startFailures = new Map();
86
+ function key(productId, focusId) {
87
+ return `${productId}:${focusId}`;
88
+ }
89
+ /** Reset the judge's memory -- tests start from a clean slate. */
90
+ export function __resetLocalJudgeForTests() {
91
+ slot = null;
92
+ startFailures.clear();
93
+ }
94
+ /** Resolves when the run in flight (if any) has settled -- tests await a run they started. */
95
+ export function __settleLocalJudgeForTests() {
96
+ return slot?.done ?? Promise.resolve();
97
+ }
98
+ /**
99
+ * The candidate's standing: its recorded verdict, else running when its run is
100
+ * in flight, else unknown (no run yet -- the gate names the run step).
101
+ */
102
+ export function readLocalCandidateStatus(productId, focusId, sha, readVerdict) {
103
+ const verdict = readVerdict(productId, focusId, sha);
104
+ if (verdict !== null)
105
+ return verdict.outcome === 'passed' ? 'green' : 'red';
106
+ if (slot !== null && slot.key === key(productId, focusId) && slot.sha === sha)
107
+ return 'running';
108
+ return 'unknown';
109
+ }
110
+ /** What a failed verdict leaves for the re-work Bug, or null when the candidate has no failed verdict. */
111
+ export function readLocalFailure(productId, focusId, sha, readVerdict) {
112
+ const verdict = readVerdict(productId, focusId, sha);
113
+ if (verdict === null || verdict.outcome !== 'failed')
114
+ return null;
115
+ return { command: verdict.command, rationale: verdict.rationale, logPath: verdict.logPath };
116
+ }
117
+ /** A run's result as a verdict, or null when the run never ran (it could not start). Pure. */
118
+ export function verdictOf(subject, sha, command, result, at) {
119
+ const timedOut = result.timedOut === true;
120
+ const exited = typeof result.exitCode === 'number';
121
+ if (!timedOut && !exited)
122
+ return null;
123
+ return {
124
+ productId: subject.productId,
125
+ focusId: subject.focusId,
126
+ sha,
127
+ outcome: !timedOut && result.outcome === 'passed' ? 'passed' : 'failed',
128
+ command,
129
+ rationale: result.rationale ?? (result.outcome === 'passed' ? 'passed' : 'failed'),
130
+ exitCode: exited ? result.exitCode : null,
131
+ timedOut,
132
+ logPath: result.logPath ?? null,
133
+ at,
134
+ };
135
+ }
136
+ async function runToVerdict(subject, sha, judge, checkout, deps) {
137
+ const k = `${key(subject.productId, subject.focusId)}:${sha}`;
138
+ try {
139
+ const result = await deps.run(judge, checkout.path, { deliveryId: `completion-${subject.focusId}`, startedAtMs: deps.now() });
140
+ if (checkout.dependenciesMoved()) {
141
+ log.warn(`[completion-merge] "${subject.focusName}": dependencies moved during the local run of ${sha.slice(0, 9)}; verdict discarded, re-run next tick`);
142
+ return;
143
+ }
144
+ const verdict = verdictOf(subject, sha, judge.command, result, deps.now());
145
+ if (verdict === null) {
146
+ startFailures.set(k, deps.now());
147
+ log.warn(`[completion-merge] "${subject.focusName}": the local completion run of ${sha.slice(0, 9)} did not run (${result.rationale ?? 'no exit'}); retried after a backoff`);
148
+ return;
149
+ }
150
+ deps.writeVerdict(verdict);
151
+ log.info(`[completion-merge] "${subject.focusName}": local completion run of ${sha.slice(0, 9)} ${verdict.outcome.toUpperCase()} (\`${judge.command}\`: ${verdict.rationale})${verdict.logPath ? ` -- log ${verdict.logPath}` : ''}`);
152
+ }
153
+ catch (err) {
154
+ startFailures.set(k, deps.now());
155
+ log.warn(`[completion-merge] "${subject.focusName}": the local completion run of ${sha.slice(0, 9)} errored: ${err.message}; retried after a backoff`);
156
+ }
157
+ finally {
158
+ checkout.dispose();
159
+ }
160
+ }
161
+ /**
162
+ * Start the local completion run of `sha` (the focus worktree's HEAD) unless one
163
+ * is in flight, the slot is taken, the worktree is not provisioned, the last
164
+ * start failed within the backoff, or no checkout of `sha` can be made. Never
165
+ * awaits the run.
166
+ */
167
+ export function startLocalCandidateRun(subject, sha, judge, deps) {
168
+ const k = key(subject.productId, subject.focusId);
169
+ if (slot !== null) {
170
+ return slot.key === k && slot.sha === sha ? { kind: 'running' } : { kind: 'busy', holder: `"${slot.focusName}" at ${slot.sha.slice(0, 9)}` };
171
+ }
172
+ const failedAt = startFailures.get(`${k}:${sha}`);
173
+ if (failedAt !== undefined && deps.now() - failedAt < LOCAL_RUN_START_BACKOFF_MS) {
174
+ return { kind: 'not_ready', reason: `the last run of ${sha.slice(0, 9)} could not start; retried after ${Math.round(LOCAL_RUN_START_BACKOFF_MS / 60_000)}m` };
175
+ }
176
+ const provisioning = deps.readProvisioning(subject.worktreePath);
177
+ if (!provisioning.ready)
178
+ return { kind: 'not_ready', reason: `the focus worktree is not provisioned (${provisioning.state}: ${provisioning.reason})` };
179
+ const checkout = deps.createCheckout(subject.worktreePath, `completion-${subject.focusId}`);
180
+ if (checkout === null)
181
+ return { kind: 'not_ready', reason: 'no detached checkout of the focus head could be made' };
182
+ if (checkout.sha !== sha) {
183
+ checkout.dispose();
184
+ return { kind: 'not_ready', reason: `the focus head is ${checkout.sha.slice(0, 9)}, not the candidate ${sha.slice(0, 9)}` };
185
+ }
186
+ const done = runToVerdict(subject, sha, judge, checkout, deps).finally(() => {
187
+ if (slot?.key === k && slot.sha === sha)
188
+ slot = null;
189
+ });
190
+ slot = { key: k, sha, focusName: subject.focusName, done };
191
+ return { kind: 'started' };
192
+ }
193
+ //# sourceMappingURL=completion-local-judge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion-local-judge.js","sourceRoot":"","sources":["../../src/merge/completion-local-judge.ts"],"names":[],"mappings":"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"}
@@ -15,13 +15,24 @@
15
15
  * push_candidate -> push the head to the focus's candidate branch so CI runs the
16
16
  * full suite on exactly that commit (ci/candidate-ref.ts).
17
17
  * wait_ci -> nothing, until CANDIDATE_CI_VERDICT_WAIT_MS has passed with
18
- * no verdict; then the missing verdict is re-work like a red.
19
- * The wait is counted from a DURABLE stamp of the candidate
20
- * (merge/completion-hold-record.ts), so a daemon restart
21
- * neither restarts nor forgets the window.
18
+ * no verdict; then the candidate is RE-PUSHED once (deleted
19
+ * and pushed again, so CI sees a fresh push), and after a
20
+ * second window with no verdict the merge simply holds --
21
+ * the stuck_focus_branch observation surfaces it. A missing
22
+ * verdict is the CI's fault, not the focus's: it is never
23
+ * re-work (no team can fix a missing verdict, and a
24
+ * converged review resets the cycle cap, so re-work would
25
+ * never end). The wait is counted from a DURABLE stamp of the
26
+ * candidate (merge/completion-hold-record.ts), so a daemon
27
+ * restart neither restarts nor forgets the window.
28
+ * run_local -> start the daemon's own run of the completion command on the
29
+ * head (merge/completion-local-judge.ts); its verdict lands
30
+ * on a later tick.
22
31
  * rework -> return the focus to coding: one Bug on the owning delivery,
23
32
  * re-opened to verify_failed (merge/candidate-rework.ts). No
24
33
  * escalation is filed.
34
+ * rework_local -> the same, for a candidate that failed the local run; the
35
+ * Bug names the command, its result and the log.
25
36
  *
26
37
  * Integration never moves here; a green candidate is admitted by the gate and
27
38
  * fast-forwarded by the caller's normal merge.
@@ -29,7 +40,8 @@
29
40
  * @module merge/completion-merge-driver
30
41
  */
31
42
  import type { CompletionMergeStep } from './completion-merge-gate.js';
32
- /** How long a pushed candidate may sit with no published verdict before it is re-work. */
43
+ import type { LocalRunStart, LocalSuiteFailure } from './completion-local-judge.js';
44
+ /** How long a pushed candidate may sit with no published verdict before it is re-pushed (once). */
33
45
  export declare const CANDIDATE_CI_VERDICT_WAIT_MS: number;
34
46
  /** The focus branch a step acts on. */
35
47
  export interface CompletionMergeSubject {
@@ -39,7 +51,9 @@ export interface CompletionMergeSubject {
39
51
  branchName: string;
40
52
  }
41
53
  /** Why a candidate is returned to the focus as re-work. */
42
- export type CandidateReworkCause = 'red' | 'no_verdict' | 'conflict' | 'unreviewed_merge';
54
+ export type CandidateReworkCause = 'red' | 'local_red' | 'conflict' | 'unreviewed_merge';
55
+ /** What a re-work Bug is told beyond its cause: a conflict's paths, or a failed local run. */
56
+ export type CandidateReworkDetail = CompletionMergeConflict | LocalSuiteFailure;
43
57
  /** What a conflicting final merge left for the team: the paths, and the integration commits the branch lacks. */
44
58
  export interface CompletionMergeConflict {
45
59
  integrationBranch: string;
@@ -77,8 +91,18 @@ export interface CompletionMergeDriverDeps {
77
91
  * becomes re-work.
78
92
  */
79
93
  candidateWaitSince: (sha: string, now: number) => number | null;
94
+ /** Whether the focus's candidate `sha` was already re-pushed for a missing verdict. */
95
+ candidateRepushed: (sha: string) => boolean;
96
+ /** Record that `sha` was re-pushed at `now`, restarting its wait window. */
97
+ markCandidateRepushed: (sha: string, now: number) => void;
98
+ /** Delete and re-push the focus's candidate branch at `sha`, so CI sees a fresh push; false on failure. */
99
+ repushCandidate: (repoPath: string, focusId: string, sha: string) => boolean;
100
+ /** Start the daemon's local completion run of `sha` (never awaited). */
101
+ startLocalRun: (sha: string) => LocalRunStart;
102
+ /** What a failed local run of `sha` recorded, for the re-work Bug. */
103
+ readLocalFailure: (sha: string) => LocalSuiteFailure | null;
80
104
  /** Return the focus to coding as re-work for the candidate at `sha`. */
81
- routeRework: (subject: CompletionMergeSubject, sha: string, cause: CandidateReworkCause, conflict: CompletionMergeConflict | null) => Promise<CandidateReworkOutcome>;
105
+ routeRework: (subject: CompletionMergeSubject, sha: string, cause: CandidateReworkCause, detail: CandidateReworkDetail | null) => Promise<CandidateReworkOutcome>;
82
106
  now: () => number;
83
107
  }
84
108
  /** Reset the driver's memory -- tests start from a clean slate. */
@@ -1 +1 @@
1
- {"version":3,"file":"completion-merge-driver.d.ts","sourceRoot":"","sources":["../../src/merge/completion-merge-driver.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAKtE,0FAA0F;AAC1F,eAAO,MAAM,4BAA4B,QAAqB,CAAC;AAE/D,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,2DAA2D;AAC3D,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,YAAY,GAAG,UAAU,GAAG,kBAAkB,CAAC;AAE1F,iHAAiH;AACjH,MAAM,WAAW,uBAAuB;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,4DAA4D;IAC5D,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,cAAc,GAAG,cAAc,CAAC;AAElF,2GAA2G;AAC3G,MAAM,MAAM,yBAAyB,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,uBAAuB,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,oGAAoG;AACpG,MAAM,WAAW,yBAAyB;IACxC,+GAA+G;IAC/G,QAAQ,EAAE,MAAM,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACnD,8EAA8E;IAC9E,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3E;;;;;;OAMG;IACH,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAChE,wEAAwE;IACxE,WAAW,EAAE,CAAC,OAAO,EAAE,sBAAsB,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,uBAAuB,GAAG,IAAI,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACtK,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAUD,mEAAmE;AACnE,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D;AAED,6GAA6G;AAC7G,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAuDD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,yBAAyB,CAAC,CAgBpC"}
1
+ {"version":3,"file":"completion-merge-driver.d.ts","sourceRoot":"","sources":["../../src/merge/completion-merge-driver.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  // PINNED-BY-END
9
9
  import { createLogger } from '../log/daemon-log.js';
10
10
  const log = createLogger({ module: 'completion-merge' });
11
- /** How long a pushed candidate may sit with no published verdict before it is re-work. */
11
+ /** How long a pushed candidate may sit with no published verdict before it is re-pushed (once). */
12
12
  export const CANDIDATE_CI_VERDICT_WAIT_MS = 3 * 60 * 60 * 1000;
13
13
  /**
14
14
  * The wait's fallback clock: when this process first saw each candidate waiting,
@@ -26,12 +26,12 @@ function key(subject, sha) {
26
26
  }
27
27
  const CAUSE_TEXT = {
28
28
  red: 'is RED in CI',
29
- no_verdict: 'got no CI verdict',
29
+ local_red: 'FAILED the local completion run',
30
30
  conflict: 'conflicts with the integration tip',
31
31
  unreviewed_merge: 'carries a conflict-deciding merge no review judged',
32
32
  };
33
- async function rework(subject, sha, cause, conflict, deps) {
34
- const outcome = await deps.routeRework(subject, sha, cause, conflict);
33
+ async function rework(subject, sha, cause, detail, deps) {
34
+ const outcome = await deps.routeRework(subject, sha, cause, detail);
35
35
  if (outcome === 'not_reopened') {
36
36
  log.warn(`[completion-merge] "${subject.focusName}": ${sha.slice(0, 9)} ${CAUSE_TEXT[cause]} but no work was re-opened -- retried next tick`);
37
37
  return { reworked: false };
@@ -56,13 +56,37 @@ function fallbackSince(subject, sha, now) {
56
56
  log.warn(`[completion-merge] "${subject.focusName}": the candidate-wait record is unusable; counting ${sha.slice(0, 9)}'s wait from this process's first sighting`);
57
57
  return since;
58
58
  }
59
- /** wait_ci: re-work once the durable wait for `headSha` has run past the window. */
60
- async function waitForVerdict(subject, headSha, deps) {
59
+ /**
60
+ * wait_ci: once the durable wait for `headSha` has run past the window, re-push
61
+ * the candidate ONCE; past a second window, hold (module doc). Never re-work.
62
+ */
63
+ function waitForVerdict(subject, headSha, deps) {
61
64
  const now = deps.now();
62
65
  const since = deps.candidateWaitSince(headSha, now) ?? fallbackSince(subject, headSha, now);
63
66
  if (now - since < CANDIDATE_CI_VERDICT_WAIT_MS)
64
67
  return NOTHING;
65
- return rework(subject, headSha, 'no_verdict', null, deps);
68
+ if (deps.candidateRepushed(headSha)) {
69
+ log.debug(`[completion-merge] "${subject.focusName}": CI published no verdict for ${headSha.slice(0, 9)} even after a re-push -- holding (not re-work: a missing verdict is CI's fault)`);
70
+ return NOTHING;
71
+ }
72
+ const pushed = deps.repushCandidate(subject.repoPath, subject.focusId, headSha);
73
+ if (pushed) {
74
+ deps.markCandidateRepushed(headSha, now);
75
+ fallbackWaitSince.set(key(subject, headSha), now);
76
+ }
77
+ log.warn(`[completion-merge] "${subject.focusName}": CI published no verdict for ${headSha.slice(0, 9)} within ${Math.round(CANDIDATE_CI_VERDICT_WAIT_MS / 3_600_000)}h -- ${pushed ? 're-pushed the candidate once so CI sees a fresh push' : 'the re-push failed; retried next tick'}`);
78
+ return NOTHING;
79
+ }
80
+ /** run_local: start the daemon's run of the completion command on the head. */
81
+ function runLocal(subject, headSha, deps) {
82
+ const start = deps.startLocalRun(headSha);
83
+ if (start.kind === 'started')
84
+ log.info(`[completion-merge] "${subject.focusName}": started the local completion run of ${headSha.slice(0, 9)}`);
85
+ else if (start.kind === 'busy')
86
+ log.debug(`[completion-merge] "${subject.focusName}": local completion run of ${headSha.slice(0, 9)} waits for ${start.holder}`);
87
+ else if (start.kind === 'not_ready')
88
+ log.debug(`[completion-merge] "${subject.focusName}": local completion run of ${headSha.slice(0, 9)} not started: ${start.reason}`);
89
+ return NOTHING;
66
90
  }
67
91
  /**
68
92
  * Take the step a held completion merge names, and say whether it returned the
@@ -77,6 +101,10 @@ export async function driveCompletionMergeStep(subject, step, headSha, deps) {
77
101
  return await rework(subject, headSha, 'unreviewed_merge', null, deps);
78
102
  if (step === 'rework')
79
103
  return await rework(subject, headSha, 'red', null, deps);
104
+ if (step === 'rework_local')
105
+ return await rework(subject, headSha, 'local_red', deps.readLocalFailure(headSha), deps);
106
+ if (step === 'run_local')
107
+ return runLocal(subject, headSha, deps);
80
108
  if (step === 'push_candidate') {
81
109
  const pushed = deps.pushCandidate(subject.repoPath, subject.focusId, headSha);
82
110
  if (pushed && deps.candidateWaitSince(headSha, deps.now()) === null)
@@ -84,7 +112,7 @@ export async function driveCompletionMergeStep(subject, step, headSha, deps) {
84
112
  log.info(`[completion-merge] "${subject.focusName}": ${pushed ? 'pushed' : 'could not push'} integration candidate ${headSha.slice(0, 9)} for CI`);
85
113
  return NOTHING;
86
114
  }
87
- return await waitForVerdict(subject, headSha, deps);
115
+ return waitForVerdict(subject, headSha, deps);
88
116
  }
89
117
  catch (err) {
90
118
  log.warn(`[completion-merge] "${subject.focusName}": ${step} for ${headSha.slice(0, 9)} failed: ${err.message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"completion-merge-driver.js","sourceRoot":"","sources":["../../src/merge/completion-merge-driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"completion-merge-driver.js","sourceRoot":"","sources":["../../src/merge/completion-merge-driver.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  import type { CiRunStatus } from '../ci/ci-status.js';
2
+ import { type CompletionJudge } from './completion-judge.js';
3
+ import type { LocalCandidateStatus } from './completion-local-judge.js';
2
4
  import type { ApprovalGateState } from './approval-gate-state.js';
3
5
  import { type BranchMergeGateTarget, type GatedAnchor, type MergeGateTarget, type UnmergedCommit } from './merge-gate-coverage.js';
4
6
  /** The step that advances a held completion merge; driven by the merge-back loop only. */
5
- export type CompletionMergeStep = 'merge_out' | 'review_merge' | 'push_candidate' | 'wait_ci' | 'rework';
7
+ export type CompletionMergeStep = 'merge_out' | 'review_merge' | 'push_candidate' | 'wait_ci' | 'rework' | 'run_local' | 'rework_local';
6
8
  /** A merge the gate holds (with why, and the step that advances it), or one it admits at an exact commit. */
7
9
  export type MergeGateDecision = {
8
10
  hold: string;
@@ -46,8 +48,14 @@ export interface CompletionMergeGateDeps {
46
48
  readCandidateStatus: (target: MergeGateTarget, sha: string) => CiRunStatus;
47
49
  /** The sha the focus's candidate branch points at on the remote; null when none / unreadable. */
48
50
  readPushedCandidateSha: (target: MergeGateTarget) => string | null;
51
+ /** The completion judge the candidate commit's telora.verify.json declares. */
52
+ resolveCompletionJudge: (target: MergeGateTarget, sha: string) => CompletionJudge;
53
+ /** The candidate's standing under the local judge (its recorded verdict, or its run in flight). */
54
+ readLocalCandidateStatus: (target: MergeGateTarget, sha: string) => LocalCandidateStatus;
49
55
  }
50
- /** Step 1: every delivery done (cancelled is not done), at least one delivery. Pure. */
56
+ /** Whether a delivery in this status still owes work, and so holds its focus's completion merge. Pure. */
57
+ export declare function deliveryHoldsCompletion(executionStatus: string | null): boolean;
58
+ /** Step 1: at least one delivery done, and none still owing work (module doc). Pure. */
51
59
  export declare function focusCompletionHold(deliveries: readonly CompletionDeliveryFact[]): string | null;
52
60
  /** The merges after the approved commit that git cannot prove clean (they decided a conflict, or edited). Pure. */
53
61
  export declare function unreviewedMergesAfter(commits: readonly UnmergedCommit[], approval: GatedAnchor): UnmergedCommit[];
@@ -1 +1 @@
1
- {"version":3,"file":"completion-merge-gate.d.ts","sourceRoot":"","sources":["../../src/merge/completion-merge-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"completion-merge-gate.d.ts","sourceRoot":"","sources":["../../src/merge/completion-merge-gate.ts"],"names":[],"mappings":"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"}
@@ -21,10 +21,12 @@
21
21
  * integration once, at completion".
22
22
  *
23
23
  * The decision, in order (the first that fails holds):
24
- * 1. COMPLETE: every focus delivery is done -- a cancelled one too holds, as
25
- * the agreed criterion reads ("every delivery done"), and so does a focus
26
- * with no delivery. Delivery facts, never product_focuses.status (humans
27
- * own focus completion).
24
+ * 1. COMPLETE: at least one focus delivery is done and none is still owed --
25
+ * cancelled, planning and draft deliveries do not hold (none of them has
26
+ * unfinished work on the branch: a cancelled one was dropped, a planning
27
+ * one -- recorded new intent included -- never started), while queued,
28
+ * coding, verify, verify_failed and paused ones do. Delivery facts, never
29
+ * product_focuses.status (humans own focus completion).
28
30
  * 2. APPROVED HEAD: the focus's newest current approval
29
31
  * (queries/focus-review/focus-review-approval.ts) is on the branch, the gate
30
32
  * did not fail at it (merge/approval-gate-state.ts), and every commit after it
@@ -39,8 +41,15 @@
39
41
  * the next step is MERGE_OUT -- one automatic merge, kept only when clean; a
40
42
  * conflict is aborted and handed to the team (merge/focus-merge.ts
41
43
  * attemptCompletionMergeOut). No resolver decides a conflict on this path.
42
- * 4. GREEN CANDIDATE: the head's refs/ci-status reads success (ci/candidate-ref.ts).
43
- * Not pushed yet -> PUSH_CANDIDATE; pushed, no verdict -> WAIT_CI; red -> REWORK.
44
+ * 4. JUDGED CANDIDATE, by the judge the head's telora.verify.json declares
45
+ * (merge/completion-judge.ts):
46
+ * - ci: the head's refs/ci-status reads success (ci/candidate-ref.ts). Not
47
+ * pushed yet -> PUSH_CANDIDATE; pushed, no verdict -> WAIT_CI; red -> REWORK.
48
+ * - local: the daemon's run of the completion command on the head passed
49
+ * (merge/completion-local-judge.ts). No run yet -> RUN_LOCAL; running ->
50
+ * hold; failed -> REWORK_LOCAL.
51
+ * - none (no manifest): admitted on the approval alone, and the admission says so.
52
+ * - unreadable: hold (fail closed), naming why.
44
53
  * A hold carries the step that would advance it (`next`) for the merge-back loop,
45
54
  * which is the one driver of those steps; the other entry points just hold.
46
55
  * An admission names the exact commit that passed (`candidateSha`), and
@@ -50,16 +59,28 @@
50
59
  * @module merge/completion-merge-gate
51
60
  */
52
61
  import { DELIVERY_STATUS } from '@telora/daemon-core';
62
+ import { describeCompletionJudge } from './completion-judge.js';
53
63
  import { stopFires } from '../supervision/stop-registry.js';
54
64
  import { judgeMergeGateCoverage, } from './merge-gate-coverage.js';
55
- /** Step 1: every delivery done (cancelled is not done), at least one delivery. Pure. */
65
+ /** Statuses that owe no work on the branch: they never hold a completion merge. */
66
+ const NOT_OWED = new Set([
67
+ DELIVERY_STATUS.DONE,
68
+ DELIVERY_STATUS.CANCELLED,
69
+ DELIVERY_STATUS.PLANNING,
70
+ DELIVERY_STATUS.DRAFT,
71
+ ]);
72
+ /** Whether a delivery in this status still owes work, and so holds its focus's completion merge. Pure. */
73
+ export function deliveryHoldsCompletion(executionStatus) {
74
+ return executionStatus === null || !NOT_OWED.has(executionStatus);
75
+ }
76
+ /** Step 1: at least one delivery done, and none still owing work (module doc). Pure. */
56
77
  export function focusCompletionHold(deliveries) {
57
- const open = deliveries.filter((d) => d.executionStatus !== DELIVERY_STATUS.DONE);
78
+ const open = deliveries.filter((d) => deliveryHoldsCompletion(d.executionStatus));
58
79
  if (open.length > 0) {
59
80
  const names = open.slice(0, 3).map((d) => `"${d.name}" (${d.executionStatus ?? 'unknown'})`).join(', ');
60
81
  return `the focus is not complete: ${open.length} delivery(ies) not done -- ${names}${open.length > 3 ? ', ...' : ''}`;
61
82
  }
62
- if (deliveries.length === 0)
83
+ if (!deliveries.some((d) => d.executionStatus === DELIVERY_STATUS.DONE))
63
84
  return 'the focus is not complete: no delivery is done';
64
85
  return null;
65
86
  }
@@ -102,7 +123,30 @@ async function readApproval(target, deps) {
102
123
  }
103
124
  return { ...resolved, source: state === 'auto_approval' ? 'auto_approval' : 'approved_review' };
104
125
  }
105
- /** Steps 3-4: the candidate. Returns the admission when the head is green. */
126
+ /** Step 4 under CI: the head's refs/ci-status. */
127
+ function ciStage(target, head, deps) {
128
+ const status = deps.readCandidateStatus(target, head);
129
+ if (status === 'green')
130
+ return { candidateSha: head, judgedBy: `candidate ${head.slice(0, 9)} green` };
131
+ if (status === 'red')
132
+ return { hold: `CI is RED on the integration candidate ${head.slice(0, 9)}`, next: 'rework', headSha: head };
133
+ if (deps.readPushedCandidateSha(target) !== head) {
134
+ return { hold: `the integration candidate ${head.slice(0, 9)} has not been pushed for CI`, next: 'push_candidate', headSha: head };
135
+ }
136
+ return { hold: `CI has not judged the integration candidate ${head.slice(0, 9)} yet`, next: 'wait_ci', headSha: head };
137
+ }
138
+ /** Step 4 under the local judge: the daemon's run of the completion command on the head. */
139
+ function localStage(target, head, judge, deps) {
140
+ const status = deps.readLocalCandidateStatus(target, head);
141
+ if (status === 'green')
142
+ return { candidateSha: head, judgedBy: `candidate ${head.slice(0, 9)} passed \`${judge.command}\` locally` };
143
+ if (status === 'red')
144
+ return { hold: `the integration candidate ${head.slice(0, 9)} FAILED \`${judge.command}\` locally`, next: 'rework_local', headSha: head };
145
+ if (status === 'running')
146
+ return { hold: `the daemon is running \`${judge.command}\` on the integration candidate ${head.slice(0, 9)}` };
147
+ return { hold: `the integration candidate ${head.slice(0, 9)} has not been run through \`${judge.command}\` yet`, next: 'run_local', headSha: head };
148
+ }
149
+ /** Steps 3-4: the candidate. Returns the admission when the head is judged good. */
106
150
  function candidateStage(target, deps) {
107
151
  const head = deps.resolveBranchHead(target.repoPath, target.branchName);
108
152
  if (head === null)
@@ -113,15 +157,17 @@ function candidateStage(target, deps) {
113
157
  if (!deps.isAncestor(target.repoPath, tip, head)) {
114
158
  return { hold: `${target.branchName} does not contain the ${target.integrationBranch} tip ${tip.slice(0, 9)} yet`, next: 'merge_out', headSha: head };
115
159
  }
116
- const status = deps.readCandidateStatus(target, head);
117
- if (status === 'green')
118
- return { candidateSha: head };
119
- if (status === 'red')
120
- return { hold: `CI is RED on the integration candidate ${head.slice(0, 9)}`, next: 'rework', headSha: head };
121
- if (deps.readPushedCandidateSha(target) !== head) {
122
- return { hold: `the integration candidate ${head.slice(0, 9)} has not been pushed for CI`, next: 'push_candidate', headSha: head };
160
+ const judge = deps.resolveCompletionJudge(target, head);
161
+ switch (judge.kind) {
162
+ case 'ci':
163
+ return ciStage(target, head, deps);
164
+ case 'local':
165
+ return localStage(target, head, judge, deps);
166
+ case 'none':
167
+ return { candidateSha: head, judgedBy: `candidate ${head.slice(0, 9)} judged by no suite -- ${describeCompletionJudge(judge)}; merged on the review approval alone` };
168
+ case 'unreadable':
169
+ return { hold: `the completion judge of the integration candidate ${head.slice(0, 9)} cannot be read: ${judge.reason}` };
123
170
  }
124
- return { hold: `CI has not judged the integration candidate ${head.slice(0, 9)} yet`, next: 'wait_ci', headSha: head };
125
171
  }
126
172
  async function decide(target, deps) {
127
173
  const incomplete = focusCompletionHold(await deps.getFocusDeliveries(target.focusId));
@@ -150,7 +196,7 @@ async function decide(target, deps) {
150
196
  return {
151
197
  hold: null,
152
198
  candidateSha: candidate.candidateSha,
153
- admittedBy: `completion: ${approval.source} ${approval.sha.slice(0, 9)}, candidate ${candidate.candidateSha.slice(0, 9)} green`,
199
+ admittedBy: `completion: ${approval.source} ${approval.sha.slice(0, 9)}, ${candidate.judgedBy}`,
154
200
  };
155
201
  }
156
202
  /**