@telora/daemon 0.23.15 → 0.23.17
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/build-info.json +4 -3
- package/dist/backends/agent-backend.d.ts +8 -1
- package/dist/backends/agent-backend.d.ts.map +1 -1
- package/dist/backends/claude/claude-backend.d.ts.map +1 -1
- package/dist/backends/claude/claude-backend.js +4 -0
- package/dist/backends/claude/claude-backend.js.map +1 -1
- package/dist/backends/codex/codex-backend.d.ts.map +1 -1
- package/dist/backends/codex/codex-backend.js +10 -0
- package/dist/backends/codex/codex-backend.js.map +1 -1
- package/dist/ci/candidate-ref.d.ts +13 -0
- package/dist/ci/candidate-ref.d.ts.map +1 -1
- package/dist/ci/candidate-ref.js +18 -0
- package/dist/ci/candidate-ref.js.map +1 -1
- package/dist/composition/execution-merge-context.d.ts +7 -1
- package/dist/composition/execution-merge-context.d.ts.map +1 -1
- package/dist/composition/execution-merge-context.js +42 -6
- package/dist/composition/execution-merge-context.js.map +1 -1
- package/dist/composition/execution-recovery-context.d.ts.map +1 -1
- package/dist/composition/execution-recovery-context.js +2 -0
- package/dist/composition/execution-recovery-context.js.map +1 -1
- package/dist/config/model-resolution.d.ts +25 -1
- package/dist/config/model-resolution.d.ts.map +1 -1
- package/dist/config/model-resolution.js +26 -1
- package/dist/config/model-resolution.js.map +1 -1
- package/dist/focus/crash-recovery.d.ts +7 -0
- package/dist/focus/crash-recovery.d.ts.map +1 -1
- package/dist/focus/crash-recovery.js +26 -13
- package/dist/focus/crash-recovery.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +1 -0
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/focus-poller-spawn.d.ts.map +1 -1
- package/dist/focus/focus-poller-spawn.js +3 -1
- package/dist/focus/focus-poller-spawn.js.map +1 -1
- package/dist/merge/candidate-rework.d.ts +3 -3
- package/dist/merge/candidate-rework.d.ts.map +1 -1
- package/dist/merge/candidate-rework.js +30 -10
- package/dist/merge/candidate-rework.js.map +1 -1
- package/dist/merge/completion-hold-record.d.ts +16 -0
- package/dist/merge/completion-hold-record.d.ts.map +1 -1
- package/dist/merge/completion-hold-record.js +60 -1
- package/dist/merge/completion-hold-record.js.map +1 -1
- package/dist/merge/completion-judge.d.ts +44 -0
- package/dist/merge/completion-judge.d.ts.map +1 -0
- package/dist/merge/completion-judge.js +36 -0
- package/dist/merge/completion-judge.js.map +1 -0
- package/dist/merge/completion-local-judge.d.ts +90 -0
- package/dist/merge/completion-local-judge.d.ts.map +1 -0
- package/dist/merge/completion-local-judge.js +193 -0
- package/dist/merge/completion-local-judge.js.map +1 -0
- package/dist/merge/completion-merge-driver.d.ts +31 -7
- package/dist/merge/completion-merge-driver.d.ts.map +1 -1
- package/dist/merge/completion-merge-driver.js +36 -8
- package/dist/merge/completion-merge-driver.js.map +1 -1
- package/dist/merge/completion-merge-gate.d.ts +10 -2
- package/dist/merge/completion-merge-gate.d.ts.map +1 -1
- package/dist/merge/completion-merge-gate.js +65 -19
- package/dist/merge/completion-merge-gate.js.map +1 -1
- package/dist/merge/merge-back-loop.d.ts +9 -1
- package/dist/merge/merge-back-loop.d.ts.map +1 -1
- package/dist/merge/merge-back-loop.js +28 -1
- package/dist/merge/merge-back-loop.js.map +1 -1
- package/dist/queries/api-client/shared.js +1 -1
- package/dist/queries/focuses/focus-discovery.d.ts +14 -0
- package/dist/queries/focuses/focus-discovery.d.ts.map +1 -1
- package/dist/queries/focuses/focus-discovery.js +4 -0
- package/dist/queries/focuses/focus-discovery.js.map +1 -1
- package/dist/queries/org-config/local-models.d.ts.map +1 -1
- package/dist/queries/org-config/local-models.js +2 -1
- package/dist/queries/org-config/local-models.js.map +1 -1
- package/dist/queries/wire-schemas/schemas.d.ts +48 -0
- package/dist/queries/wire-schemas/schemas.d.ts.map +1 -1
- package/dist/queries/wire-schemas/schemas.js +15 -0
- package/dist/queries/wire-schemas/schemas.js.map +1 -1
- package/dist/spawn/model-routing.d.ts +13 -8
- package/dist/spawn/model-routing.d.ts.map +1 -1
- package/dist/spawn/model-routing.js +14 -8
- package/dist/spawn/model-routing.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts +7 -0
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/testing/fixtures/api-client.js +1 -1
- package/dist/testing/fixtures/completion-judge-stubs.d.ts +15 -0
- package/dist/testing/fixtures/completion-judge-stubs.d.ts.map +1 -0
- package/dist/testing/fixtures/completion-judge-stubs.js +32 -0
- package/dist/testing/fixtures/completion-judge-stubs.js.map +1 -0
- package/dist/testing/fixtures/index.d.ts.map +1 -1
- package/dist/testing/fixtures/index.js +3 -1
- package/dist/testing/fixtures/index.js.map +1 -1
- package/dist/types/focus.d.ts +9 -0
- package/dist/types/focus.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-checkout.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-checkout.js +1 -0
- package/dist/verify/deterministic-gate/verification-checkout.js.map +1 -1
- package/dist/verify/deterministic-gate/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-deterministic.js +1 -0
- package/dist/verify/deterministic-gate/verification-deterministic.js.map +1 -1
- package/dist/verify/gate-config/verification-manifest.d.ts +21 -1
- package/dist/verify/gate-config/verification-manifest.d.ts.map +1 -1
- package/dist/verify/gate-config/verification-manifest.js +25 -1
- package/dist/verify/gate-config/verification-manifest.js.map +1 -1
- package/dist/verify/run-selection/verification-default-on.d.ts +2 -0
- package/dist/verify/run-selection/verification-default-on.d.ts.map +1 -1
- package/dist/verify/run-selection/verification-default-on.js +1 -1
- package/dist/verify/run-selection/verification-default-on.js.map +1 -1
- package/dist/worktree/focus-worktree-state.d.ts.map +1 -1
- package/dist/worktree/focus-worktree-state.js +1 -0
- package/dist/worktree/focus-worktree-state.js.map +1 -1
- package/package.json +3 -3
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*
|
|
30
30
|
* @module merge/completion-hold-record
|
|
31
31
|
*/
|
|
32
|
-
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
32
|
+
import { mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
33
33
|
import { resolve } from 'node:path';
|
|
34
34
|
import { resolveGlobalStateDir } from '../daemon-state/daemon-process.js';
|
|
35
35
|
import { createLogger } from '../log/daemon-log.js';
|
|
@@ -117,6 +117,65 @@ export function stampCandidateWait(productId, focusId, sha, now, dir = holdDir()
|
|
|
117
117
|
return null;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
/** Whether the focus's candidate `sha` was already re-pushed for a missing CI verdict (merge/completion-merge-driver.ts). */
|
|
121
|
+
export function readCandidateRepushed(productId, focusId, sha, dir = holdDir()) {
|
|
122
|
+
const existing = readCandidateWait(candidateWaitPath(productId, focusId, dir));
|
|
123
|
+
return existing !== null && existing.sha === sha && existing.repushed === true;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Record that `sha` was re-pushed at `now`: its wait window restarts from `now`
|
|
127
|
+
* and it is never re-pushed again. Best-effort (an unwritable record is logged;
|
|
128
|
+
* the driver's in-memory clock still bounds the wait).
|
|
129
|
+
*/
|
|
130
|
+
export function markCandidateRepushed(productId, focusId, sha, now, dir = holdDir()) {
|
|
131
|
+
try {
|
|
132
|
+
writeAtomic(dir, candidateWaitPath(productId, focusId, dir), { productId, focusId, sha, since: now, repushed: true });
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
log.warn(`[completion-merge] candidate-wait record for focus ${focusId} cannot record the re-push: ${err.message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Whether a completion hold is recorded for the focus: its completed merge is still pending on this host. */
|
|
139
|
+
export function hasCompletionHold(productId, focusId, dir = holdDir()) {
|
|
140
|
+
try {
|
|
141
|
+
return readFirstHeldAt(completionHoldPath(productId, focusId, dir)) !== 'absent';
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// An unreadable record still says a hold was written: treat it as pending (it only ever KEEPS a branch in view).
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The focus ids with a recorded completion hold for a product, read from the
|
|
150
|
+
* record directory. A crash-recovery pass reads this to keep the worktree of a
|
|
151
|
+
* focus whose completed merge is still pending (focus/crash-recovery.ts).
|
|
152
|
+
*/
|
|
153
|
+
export function listCompletionHoldFocusIds(productId, dir = holdDir()) {
|
|
154
|
+
let names;
|
|
155
|
+
try {
|
|
156
|
+
names = readdirSync(dir);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
const out = [];
|
|
162
|
+
for (const name of names) {
|
|
163
|
+
if (!name.endsWith('.json') || name.endsWith('-candidate.json'))
|
|
164
|
+
continue;
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(readFileSync(resolve(dir, name), 'utf8'));
|
|
167
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
168
|
+
continue;
|
|
169
|
+
const rec = parsed;
|
|
170
|
+
if (rec.productId === productId && typeof rec.focusId === 'string')
|
|
171
|
+
out.push(rec.focusId);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
120
179
|
/** Forget the focus's first hold and candidate wait (it merged, or it is no longer complete). Best-effort. */
|
|
121
180
|
export function clearCompletionHold(productId, focusId, dir = holdDir()) {
|
|
122
181
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion-hold-record.js","sourceRoot":"","sources":["../../src/merge/completion-hold-record.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,uEAAuE;AACvE,8DAA8D;AAC9D,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"completion-hold-record.js","sourceRoot":"","sources":["../../src/merge/completion-hold-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What judges a completed focus's integration candidate -- the focus head merged
|
|
3
|
+
* with the integration tip -- before integration fast-forwards to it. The
|
|
4
|
+
* PRODUCT declares it (telora.verify.json `completionJudge`), read from the
|
|
5
|
+
* candidate commit itself, so the declaration travels with the code it judges:
|
|
6
|
+
*
|
|
7
|
+
* - `ci` -- the product's remote CI publishes refs/ci-status for the
|
|
8
|
+
* candidate branch (ci/candidate-ref.ts). Only a product that
|
|
9
|
+
* says so is waited on: a CI that publishes nothing would hold
|
|
10
|
+
* every finished focus forever.
|
|
11
|
+
* - `local` -- the daemon runs the product's completion command on exactly
|
|
12
|
+
* the candidate commit (merge/completion-local-judge.ts). The
|
|
13
|
+
* default for any repo that ships a manifest.
|
|
14
|
+
* - `none` -- the repo ships no manifest: no suite is declared, so the merge
|
|
15
|
+
* rests on the focus's review approval alone, and says so.
|
|
16
|
+
* - `unreadable` -- git could not read the manifest at the candidate, or the
|
|
17
|
+
* manifest is malformed: the merge holds (fail closed), naming why.
|
|
18
|
+
*
|
|
19
|
+
* Why: docs/DECISIONS.md, "The completion candidate is judged where the
|
|
20
|
+
* product declares".
|
|
21
|
+
*
|
|
22
|
+
* @module merge/completion-judge
|
|
23
|
+
*/
|
|
24
|
+
import type { JudgedCommitRead } from '../verify/run-selection/verification-default-on.js';
|
|
25
|
+
/** Default bound on a local completion run: a full suite, not a targeted gate. */
|
|
26
|
+
export declare const DEFAULT_COMPLETION_TIMEOUT_MS: number;
|
|
27
|
+
/** The judge a candidate commit declares. */
|
|
28
|
+
export type CompletionJudge = {
|
|
29
|
+
kind: 'ci';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'local';
|
|
32
|
+
command: string;
|
|
33
|
+
timeoutMs: number;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'none';
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'unreadable';
|
|
38
|
+
reason: string;
|
|
39
|
+
};
|
|
40
|
+
/** The judge the manifest read at the candidate commit declares. Pure. */
|
|
41
|
+
export declare function completionJudgeFrom(read: JudgedCommitRead): CompletionJudge;
|
|
42
|
+
/** One line naming a judge, for the merge log and holds. Pure. */
|
|
43
|
+
export declare function describeCompletionJudge(judge: CompletionJudge): string;
|
|
44
|
+
//# sourceMappingURL=completion-judge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-judge.d.ts","sourceRoot":"","sources":["../../src/merge/completion-judge.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oDAAoD,CAAC;AAE3F,kFAAkF;AAClF,eAAO,MAAM,6BAA6B,QAAiB,CAAC;AAE5D,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GACd;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAW3E;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAWtE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
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-judge.test.ts
|
|
3
|
+
// PINNED-BY-END
|
|
4
|
+
/** Default bound on a local completion run: a full suite, not a targeted gate. */
|
|
5
|
+
export const DEFAULT_COMPLETION_TIMEOUT_MS = 60 * 60 * 1000;
|
|
6
|
+
/** The judge the manifest read at the candidate commit declares. Pure. */
|
|
7
|
+
export function completionJudgeFrom(read) {
|
|
8
|
+
if ('unreadable' in read)
|
|
9
|
+
return { kind: 'unreadable', reason: read.unreadable };
|
|
10
|
+
if (read.malformed !== undefined)
|
|
11
|
+
return { kind: 'unreadable', reason: read.malformed };
|
|
12
|
+
const manifest = read.manifest;
|
|
13
|
+
if (manifest === undefined)
|
|
14
|
+
return { kind: 'none' };
|
|
15
|
+
if (manifest.completionJudge === 'ci')
|
|
16
|
+
return { kind: 'ci' };
|
|
17
|
+
return {
|
|
18
|
+
kind: 'local',
|
|
19
|
+
command: manifest.completionCommand ?? manifest.command,
|
|
20
|
+
timeoutMs: manifest.completionTimeoutMs ?? manifest.timeoutMs ?? DEFAULT_COMPLETION_TIMEOUT_MS,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** One line naming a judge, for the merge log and holds. Pure. */
|
|
24
|
+
export function describeCompletionJudge(judge) {
|
|
25
|
+
switch (judge.kind) {
|
|
26
|
+
case 'ci':
|
|
27
|
+
return 'CI (refs/ci-status)';
|
|
28
|
+
case 'local':
|
|
29
|
+
return `the daemon running \`${judge.command}\``;
|
|
30
|
+
case 'none':
|
|
31
|
+
return 'no completion suite (the repo ships no telora.verify.json)';
|
|
32
|
+
case 'unreadable':
|
|
33
|
+
return `an unreadable declaration (${judge.reason})`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=completion-judge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-judge.js","sourceRoot":"","sources":["../../src/merge/completion-judge.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,gEAAgE;AAChE,gBAAgB;AA4BhB,kFAAkF;AAClF,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAS5D,0EAA0E;AAC1E,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,IAAI,YAAY,IAAI,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACjF,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IACxF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACpD,IAAI,QAAQ,CAAC,eAAe,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC7D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,QAAQ,CAAC,iBAAiB,IAAI,QAAQ,CAAC,OAAO;QACvD,SAAS,EAAE,QAAQ,CAAC,mBAAmB,IAAI,QAAQ,CAAC,SAAS,IAAI,6BAA6B;KAC/F,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,uBAAuB,CAAC,KAAsB;IAC5D,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,IAAI;YACP,OAAO,qBAAqB,CAAC;QAC/B,KAAK,OAAO;YACV,OAAO,wBAAwB,KAAK,CAAC,OAAO,IAAI,CAAC;QACnD,KAAK,MAAM;YACT,OAAO,4DAA4D,CAAC;QACtE,KAAK,YAAY;YACf,OAAO,8BAA8B,KAAK,CAAC,MAAM,GAAG,CAAC;IACzD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { StrategyResult } from '../verify/deterministic-gate/verification-deterministic.js';
|
|
2
|
+
import type { VerifyCheckout } from '../verify/deterministic-gate/verification-checkout.js';
|
|
3
|
+
import type { WorktreeProvisioningStatus } from '../worktree/worktree-package-provisioning.js';
|
|
4
|
+
/** How long a run that could not START waits before it is tried again. */
|
|
5
|
+
export declare const LOCAL_RUN_START_BACKOFF_MS: number;
|
|
6
|
+
/** A completed run's verdict on one candidate commit. */
|
|
7
|
+
export interface LocalCompletionVerdict {
|
|
8
|
+
productId: string;
|
|
9
|
+
focusId: string;
|
|
10
|
+
sha: string;
|
|
11
|
+
outcome: 'passed' | 'failed';
|
|
12
|
+
command: string;
|
|
13
|
+
rationale: string;
|
|
14
|
+
exitCode: number | null;
|
|
15
|
+
timedOut: boolean;
|
|
16
|
+
logPath: string | null;
|
|
17
|
+
at: number;
|
|
18
|
+
}
|
|
19
|
+
/** What a failed local run leaves for the focus's re-work Bug. */
|
|
20
|
+
export interface LocalSuiteFailure {
|
|
21
|
+
command: string;
|
|
22
|
+
rationale: string;
|
|
23
|
+
logPath: string | null;
|
|
24
|
+
}
|
|
25
|
+
/** The candidate's standing under the local judge. */
|
|
26
|
+
export type LocalCandidateStatus = 'green' | 'red' | 'running' | 'unknown';
|
|
27
|
+
/** Why a start did or did not begin a run. */
|
|
28
|
+
export type LocalRunStart = {
|
|
29
|
+
kind: 'started';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'running';
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'busy';
|
|
34
|
+
holder: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'not_ready';
|
|
37
|
+
reason: string;
|
|
38
|
+
};
|
|
39
|
+
/** The focus a run judges. */
|
|
40
|
+
export interface LocalJudgeSubject {
|
|
41
|
+
productId: string;
|
|
42
|
+
focusId: string;
|
|
43
|
+
focusName: string;
|
|
44
|
+
worktreePath: string;
|
|
45
|
+
}
|
|
46
|
+
/** The judge's collaborators; bound at the composition root (composition/execution-merge-context.ts). */
|
|
47
|
+
export interface LocalJudgeDeps {
|
|
48
|
+
readProvisioning: (worktreePath: string) => WorktreeProvisioningStatus;
|
|
49
|
+
/** A detached checkout of the focus worktree's HEAD (verification-checkout.ts createVerifyCheckout). */
|
|
50
|
+
createCheckout: (worktreePath: string, checkoutKey: string) => VerifyCheckout | null;
|
|
51
|
+
run: (config: {
|
|
52
|
+
command: string;
|
|
53
|
+
timeoutMs: number;
|
|
54
|
+
}, cwd: string, logContext: {
|
|
55
|
+
deliveryId: string;
|
|
56
|
+
startedAtMs: number;
|
|
57
|
+
}) => Promise<StrategyResult>;
|
|
58
|
+
writeVerdict: (verdict: LocalCompletionVerdict) => void;
|
|
59
|
+
now: () => number;
|
|
60
|
+
}
|
|
61
|
+
/** The record file for one candidate; `dir` defaults to the global state dir. Exported for tests. */
|
|
62
|
+
export declare function localVerdictPath(productId: string, focusId: string, sha: string, dir?: string): string;
|
|
63
|
+
/** Write a verdict atomically (temp + rename). Throws when it cannot. */
|
|
64
|
+
export declare function writeLocalVerdict(verdict: LocalCompletionVerdict, dir?: string): void;
|
|
65
|
+
/** The recorded verdict on a candidate, or null when none is recorded (or it is unreadable). */
|
|
66
|
+
export declare function readLocalVerdict(productId: string, focusId: string, sha: string, dir?: string): LocalCompletionVerdict | null;
|
|
67
|
+
/** Reset the judge's memory -- tests start from a clean slate. */
|
|
68
|
+
export declare function __resetLocalJudgeForTests(): void;
|
|
69
|
+
/** Resolves when the run in flight (if any) has settled -- tests await a run they started. */
|
|
70
|
+
export declare function __settleLocalJudgeForTests(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* The candidate's standing: its recorded verdict, else running when its run is
|
|
73
|
+
* in flight, else unknown (no run yet -- the gate names the run step).
|
|
74
|
+
*/
|
|
75
|
+
export declare function readLocalCandidateStatus(productId: string, focusId: string, sha: string, readVerdict: (productId: string, focusId: string, sha: string) => LocalCompletionVerdict | null): LocalCandidateStatus;
|
|
76
|
+
/** What a failed verdict leaves for the re-work Bug, or null when the candidate has no failed verdict. */
|
|
77
|
+
export declare function readLocalFailure(productId: string, focusId: string, sha: string, readVerdict: (productId: string, focusId: string, sha: string) => LocalCompletionVerdict | null): LocalSuiteFailure | null;
|
|
78
|
+
/** A run's result as a verdict, or null when the run never ran (it could not start). Pure. */
|
|
79
|
+
export declare function verdictOf(subject: LocalJudgeSubject, sha: string, command: string, result: StrategyResult, at: number): LocalCompletionVerdict | null;
|
|
80
|
+
/**
|
|
81
|
+
* Start the local completion run of `sha` (the focus worktree's HEAD) unless one
|
|
82
|
+
* is in flight, the slot is taken, the worktree is not provisioned, the last
|
|
83
|
+
* start failed within the backoff, or no checkout of `sha` can be made. Never
|
|
84
|
+
* awaits the run.
|
|
85
|
+
*/
|
|
86
|
+
export declare function startLocalCandidateRun(subject: LocalJudgeSubject, sha: string, judge: {
|
|
87
|
+
command: string;
|
|
88
|
+
timeoutMs: number;
|
|
89
|
+
}, deps: LocalJudgeDeps): LocalRunStart;
|
|
90
|
+
//# sourceMappingURL=completion-local-judge.d.ts.map
|
|
@@ -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
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
|
|
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' | '
|
|
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,
|
|
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
|
|
1
|
+
{"version":3,"file":"completion-merge-driver.d.ts","sourceRoot":"","sources":["../../src/merge/completion-merge-driver.ts"],"names":[],"mappings":"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"}
|