toolroll 0.9.81 → 0.9.83
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 +35 -0
- package/dist/cli.js +1 -0
- package/dist/commit-extract.d.ts +18 -0
- package/dist/commit-extract.js +39 -1
- package/dist/converse.d.ts +12 -0
- package/dist/converse.js +18 -2
- package/dist/deploy-gate.js +8 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/eval-gate.d.ts +51 -0
- package/dist/eval-gate.js +199 -0
- package/dist/eval-hooks.d.ts +39 -0
- package/dist/eval-hooks.js +477 -0
- package/dist/eval-oracle-main.d.ts +1 -0
- package/dist/eval-oracle-main.js +31 -0
- package/dist/eval-oracle.d.ts +60 -0
- package/dist/eval-oracle.js +217 -0
- package/dist/eval-world.d.ts +17 -0
- package/dist/eval-world.js +97 -0
- package/dist/evals.d.ts +299 -0
- package/dist/evals.js +964 -0
- package/dist/exec.js +15 -1
- package/dist/first-tasks-child.d.ts +1 -0
- package/dist/first-tasks-child.js +11 -0
- package/dist/flow-draft.js +4 -0
- package/dist/flow-sort.js +4 -0
- package/dist/folder-look.d.ts +107 -0
- package/dist/folder-look.js +302 -0
- package/dist/github-jobs.d.ts +25 -0
- package/dist/github-jobs.js +22 -0
- package/dist/invoke.d.ts +1 -1
- package/dist/invoke.js +14 -0
- package/dist/lead.js +4 -1
- package/dist/learning-child.d.ts +1 -0
- package/dist/learning-child.js +34 -0
- package/dist/learning-look.d.ts +25 -0
- package/dist/learning-look.js +72 -0
- package/dist/memory-pass.js +4 -0
- package/dist/nightly-check.d.ts +4 -1
- package/dist/nightly-check.js +5 -2
- package/dist/nightly-github.d.ts +316 -0
- package/dist/nightly-github.js +1022 -0
- package/dist/operate.d.ts +123 -1
- package/dist/operate.js +344 -29
- package/dist/project-identity.d.ts +37 -0
- package/dist/project-identity.js +106 -0
- package/dist/project-knowledge.d.ts +4 -4
- package/dist/project-tools.d.ts +4 -1
- package/dist/project-tools.js +7 -2
- package/dist/project.d.ts +4 -1
- package/dist/project.js +11 -4
- package/dist/provider.d.ts +3 -0
- package/dist/provider.js +9 -2
- package/dist/release-adapters.d.ts +21 -21
- package/dist/release-adapters.js +52 -19
- package/dist/release-auto.js +26 -0
- package/dist/release-card.js +3 -0
- package/dist/release-cli.js +59 -3
- package/dist/release-view.d.ts +3 -0
- package/dist/release-view.js +2 -0
- package/dist/release.d.ts +16 -0
- package/dist/release.js +16 -0
- package/dist/reported-usage.d.ts +69 -0
- package/dist/reported-usage.js +97 -0
- package/dist/repos.d.ts +7 -0
- package/dist/repos.js +57 -0
- package/dist/serve.js +35 -8
- package/dist/server/chat.js +4 -0
- package/dist/server/flows.js +9 -3
- package/dist/server/guards.d.ts +4 -0
- package/dist/server/guards.js +12 -3
- package/dist/server/http.d.ts +11 -0
- package/dist/server/http.js +3 -0
- package/dist/server/pages.js +62 -88
- package/dist/server/render-pages.d.ts +2 -16
- package/dist/server/render-pages.js +1 -55
- package/dist/server/render-tasks.js +1 -0
- package/dist/server/runtime.d.ts +4 -0
- package/dist/server/runtime.js +1 -0
- package/dist/server/settings.js +14 -3
- package/dist/server/tasks.js +15 -4
- package/dist/store.d.ts +109 -1
- package/dist/store.js +394 -4
- package/dist/subagents.js +4 -0
- package/dist/subscription-chat.d.ts +2 -0
- package/dist/subscription-chat.js +52 -13
- package/dist/task-sizing.js +4 -0
- package/dist/teardown-git.d.ts +9 -2
- package/dist/teardown-git.js +56 -33
- package/dist/up-processes.d.ts +13 -0
- package/dist/up-processes.js +6 -2
- package/evals/cases/catalog/case.json +25 -0
- package/evals/cases/catalog/fixture/README.md +3 -0
- package/evals/cases/catalog/fixture/package.json +8 -0
- package/evals/cases/catalog/fixture/test/catalog.test.mjs +4 -0
- package/evals/cases/catalog/oracle.mjs +23 -0
- package/evals/cases/catalog/probe.mjs +10 -0
- package/evals/cases/catalog/source.md +9 -0
- package/evals/cases/csv-quoting/case.json +23 -0
- package/evals/cases/csv-quoting/fixture/README.md +3 -0
- package/evals/cases/csv-quoting/fixture/csv.js +1 -0
- package/evals/cases/csv-quoting/fixture/package.json +8 -0
- package/evals/cases/csv-quoting/fixture/test/csv.test.mjs +5 -0
- package/evals/cases/csv-quoting/oracle.mjs +14 -0
- package/evals/cases/csv-quoting/probe.mjs +9 -0
- package/evals/cases/csv-quoting/source.md +9 -0
- package/evals/cases/dedupe/case.json +23 -0
- package/evals/cases/dedupe/fixture/README.md +3 -0
- package/evals/cases/dedupe/fixture/dedupe.js +1 -0
- package/evals/cases/dedupe/fixture/package.json +8 -0
- package/evals/cases/dedupe/fixture/test/dedupe.test.mjs +5 -0
- package/evals/cases/dedupe/oracle.mjs +16 -0
- package/evals/cases/dedupe/probe.mjs +13 -0
- package/evals/cases/dedupe/source.md +9 -0
- package/evals/cases/endpoint/case.json +25 -0
- package/evals/cases/endpoint/fixture/README.md +3 -0
- package/evals/cases/endpoint/fixture/package.json +8 -0
- package/evals/cases/endpoint/fixture/test/endpoint.test.mjs +4 -0
- package/evals/cases/endpoint/oracle.mjs +27 -0
- package/evals/cases/endpoint/probe.mjs +10 -0
- package/evals/cases/endpoint/source.md +11 -0
- package/evals/cases/math-divide/case.json +23 -0
- package/evals/cases/math-divide/fixture/README.md +3 -0
- package/evals/cases/math-divide/fixture/package.json +8 -0
- package/evals/cases/math-divide/fixture/src/math.js +1 -0
- package/evals/cases/math-divide/fixture/test/math.test.js +5 -0
- package/evals/cases/math-divide/oracle.mjs +13 -0
- package/evals/cases/math-divide/probe.mjs +9 -0
- package/evals/cases/math-divide/source.md +9 -0
- package/evals/cases/math-multiply/case.json +23 -0
- package/evals/cases/math-multiply/fixture/README.md +3 -0
- package/evals/cases/math-multiply/fixture/package.json +8 -0
- package/evals/cases/math-multiply/fixture/src/math.js +2 -0
- package/evals/cases/math-multiply/fixture/test/math.test.js +5 -0
- package/evals/cases/math-multiply/oracle.mjs +12 -0
- package/evals/cases/math-multiply/probe.mjs +9 -0
- package/evals/cases/math-multiply/source.md +9 -0
- package/evals/cases/math-subtract/case.json +23 -0
- package/evals/cases/math-subtract/fixture/README.md +3 -0
- package/evals/cases/math-subtract/fixture/package.json +8 -0
- package/evals/cases/math-subtract/fixture/src/math.js +1 -0
- package/evals/cases/math-subtract/fixture/test/math.test.js +5 -0
- package/evals/cases/math-subtract/oracle.mjs +12 -0
- package/evals/cases/math-subtract/probe.mjs +9 -0
- package/evals/cases/math-subtract/source.md +9 -0
- package/evals/cases/merge-config/case.json +23 -0
- package/evals/cases/merge-config/fixture/README.md +3 -0
- package/evals/cases/merge-config/fixture/config.js +1 -0
- package/evals/cases/merge-config/fixture/package.json +8 -0
- package/evals/cases/merge-config/fixture/test/config.test.mjs +5 -0
- package/evals/cases/merge-config/oracle.mjs +28 -0
- package/evals/cases/merge-config/probe.mjs +15 -0
- package/evals/cases/merge-config/source.md +9 -0
- package/evals/cases/paginate/case.json +23 -0
- package/evals/cases/paginate/fixture/README.md +3 -0
- package/evals/cases/paginate/fixture/package.json +8 -0
- package/evals/cases/paginate/fixture/paginate.js +1 -0
- package/evals/cases/paginate/fixture/test/paginate.test.mjs +5 -0
- package/evals/cases/paginate/oracle.mjs +18 -0
- package/evals/cases/paginate/probe.mjs +13 -0
- package/evals/cases/paginate/source.md +9 -0
- package/evals/cases/round2-asks/case.json +24 -0
- package/evals/cases/round2-asks/fixture/README.md +3 -0
- package/evals/cases/round2-asks/fixture/package.json +8 -0
- package/evals/cases/round2-asks/fixture/src/math.js +1 -0
- package/evals/cases/round2-asks/fixture/test/math.test.js +5 -0
- package/evals/cases/round2-asks/source.md +11 -0
- package/evals/manifest.json +15 -0
- package/package.json +2 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Started only by learning-look.ts: one project's learning recovery (project-learning.ts acceptedIdentities and
|
|
3
|
+
* recoverLearning) in a process of its own, so its synchronous reads of the project's Git folder can never hold the
|
|
4
|
+
* console's thread. It opens the database without migrating, does that one project, and ends; the console kills it
|
|
5
|
+
* at its deadline. Its one argument is JSON naming the database, the evidence folder and the project.
|
|
6
|
+
*/
|
|
7
|
+
import { acceptedIdentities, recoverLearning } from "./project-learning.js";
|
|
8
|
+
import { openStoreNoMigrate } from "./store.js";
|
|
9
|
+
const input = JSON.parse(process.argv[2] ?? "null");
|
|
10
|
+
const text = (value) => typeof value === "string" && value !== "" && !value.includes("\0");
|
|
11
|
+
if (input === null || !text(input.databaseFile) || !text(input.evidenceRoot) || !text(input.repo))
|
|
12
|
+
process.exit(2);
|
|
13
|
+
const opened = openStoreNoMigrate(input.databaseFile);
|
|
14
|
+
if (!opened.ok)
|
|
15
|
+
process.exit(3);
|
|
16
|
+
let code = 0;
|
|
17
|
+
try {
|
|
18
|
+
try {
|
|
19
|
+
acceptedIdentities(opened.store, input.repo);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
code = 1;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
recoverLearning(opened.store, input.evidenceRoot, input.repo);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
code = 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
opened.store.close();
|
|
33
|
+
}
|
|
34
|
+
process.exit(code);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type FirstTaskSuggestion } from "./first-run.js";
|
|
2
|
+
/** How long one project's recovery may take before it is stopped (the next pass tries again). */
|
|
3
|
+
export declare const LEARNING_LOOK_MS = 60000;
|
|
4
|
+
/** How long reading one project's first-task suggestions may take. */
|
|
5
|
+
export declare const FIRST_TASKS_LOOK_MS = 20000;
|
|
6
|
+
export type LearningOutcome = "done" | "failed" | "no-answer";
|
|
7
|
+
export type LearningRunner = (input: {
|
|
8
|
+
databaseFile: string;
|
|
9
|
+
evidenceRoot: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
}, deadlineMs: number) => Promise<LearningOutcome>;
|
|
12
|
+
/** The production learning runner: learning-child in its own process. */
|
|
13
|
+
export declare const learningChild: LearningRunner;
|
|
14
|
+
/** Recover learning for each project in turn, each in its own bounded process. */
|
|
15
|
+
export declare function recoverLearningOffThread(input: {
|
|
16
|
+
databaseFile: string;
|
|
17
|
+
evidenceRoot: string;
|
|
18
|
+
repos: readonly string[];
|
|
19
|
+
}, options?: {
|
|
20
|
+
runner?: LearningRunner;
|
|
21
|
+
deadlineMs?: number;
|
|
22
|
+
}): Promise<Map<string, LearningOutcome>>;
|
|
23
|
+
/** One project's first-task suggestions (first-run.ts findFirstTasks: its issues through gh, its TODOs through the
|
|
24
|
+
* hardened git) read in a process of its own. Rejects when it failed or didn't answer: the caller keeps the generic ones. */
|
|
25
|
+
export declare function firstTasksOffThread(repo: string, deadlineMs?: number): Promise<FirstTaskSuggestion[]>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Work that reads a project's own folder synchronously, run in a process of its own and killed at a deadline, so a
|
|
3
|
+
* folder that doesn't answer (a disconnected drive, an iCloud folder macOS is holding) costs that one piece of work,
|
|
4
|
+
* never the console's thread: learning recovery (learning-child.ts) and Chat's first-task suggestions
|
|
5
|
+
* (first-tasks-child.ts).
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import {} from "./first-run.js";
|
|
10
|
+
/** How long one project's recovery may take before it is stopped (the next pass tries again). */
|
|
11
|
+
export const LEARNING_LOOK_MS = 60_000;
|
|
12
|
+
/** How long reading one project's first-task suggestions may take. */
|
|
13
|
+
export const FIRST_TASKS_LOOK_MS = 20_000;
|
|
14
|
+
/** Only what git and gh need from the environment (no NODE_OPTIONS, no other secrets). */
|
|
15
|
+
const CHILD_ENV = ["PATH", "HOME", "TMPDIR", "LANG", "USER", "XDG_CONFIG_HOME", "GH_TOKEN", "GITHUB_TOKEN", "GH_HOST", "GH_CONFIG_DIR"];
|
|
16
|
+
/** One of this build's own child entries, given `input` as JSON, killed at the deadline. Its stdout, bounded. */
|
|
17
|
+
function ownChild(name, input, deadlineMs) {
|
|
18
|
+
return new Promise(resolveOutcome => {
|
|
19
|
+
const source = import.meta.url.endsWith(".ts");
|
|
20
|
+
const entry = fileURLToPath(new URL(source ? `./${name}.ts` : `./${name}.js`, import.meta.url));
|
|
21
|
+
const env = { ELECTRON_RUN_AS_NODE: "1" };
|
|
22
|
+
for (const one of CHILD_ENV)
|
|
23
|
+
if (process.env[one] !== undefined)
|
|
24
|
+
env[one] = process.env[one];
|
|
25
|
+
const child = spawn(process.execPath, [...(source ? ["--import", import.meta.resolve("tsx")] : []), entry, JSON.stringify(input)], {
|
|
26
|
+
stdio: ["ignore", "pipe", "ignore"], env, killSignal: "SIGKILL",
|
|
27
|
+
});
|
|
28
|
+
child.unref();
|
|
29
|
+
const chunks = [];
|
|
30
|
+
let size = 0;
|
|
31
|
+
let settled = false;
|
|
32
|
+
const settle = (outcome) => {
|
|
33
|
+
if (settled)
|
|
34
|
+
return;
|
|
35
|
+
settled = true;
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
child.stdout.destroy();
|
|
38
|
+
resolveOutcome({ outcome, stdout: Buffer.concat(chunks).toString("utf8") });
|
|
39
|
+
};
|
|
40
|
+
const timer = setTimeout(() => { child.kill("SIGKILL"); settle("no-answer"); }, deadlineMs);
|
|
41
|
+
timer.unref();
|
|
42
|
+
child.stdout.on("data", (chunk) => { size += chunk.length; if (size <= 1024 * 1024)
|
|
43
|
+
chunks.push(chunk); });
|
|
44
|
+
child.once("error", () => settle("failed"));
|
|
45
|
+
child.once("close", code => settle(code === 0 ? "done" : "failed"));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** The production learning runner: learning-child in its own process. */
|
|
49
|
+
export const learningChild = async (input, deadlineMs) => (await ownChild("learning-child", input, deadlineMs)).outcome;
|
|
50
|
+
/** Recover learning for each project in turn, each in its own bounded process. */
|
|
51
|
+
export async function recoverLearningOffThread(input, options = {}) {
|
|
52
|
+
const outcomes = new Map();
|
|
53
|
+
for (const repo of input.repos) {
|
|
54
|
+
outcomes.set(repo, await (options.runner ?? learningChild)({ databaseFile: input.databaseFile, evidenceRoot: input.evidenceRoot, repo }, options.deadlineMs ?? LEARNING_LOOK_MS));
|
|
55
|
+
}
|
|
56
|
+
return outcomes;
|
|
57
|
+
}
|
|
58
|
+
/** One project's first-task suggestions (first-run.ts findFirstTasks: its issues through gh, its TODOs through the
|
|
59
|
+
* hardened git) read in a process of its own. Rejects when it failed or didn't answer: the caller keeps the generic ones. */
|
|
60
|
+
export async function firstTasksOffThread(repo, deadlineMs = FIRST_TASKS_LOOK_MS) {
|
|
61
|
+
const ran = await ownChild("first-tasks-child", { repo }, deadlineMs);
|
|
62
|
+
if (ran.outcome !== "done")
|
|
63
|
+
throw new Error(`the first-task read ${ran.outcome === "no-answer" ? "didn't answer" : "failed"}`);
|
|
64
|
+
const parsed = JSON.parse(ran.stdout);
|
|
65
|
+
if (!Array.isArray(parsed))
|
|
66
|
+
throw new Error("the first-task read answered something else");
|
|
67
|
+
return parsed.flatMap((one) => {
|
|
68
|
+
const row = one;
|
|
69
|
+
return row !== null && typeof row === "object" && (row.source === "issue" || row.source === "todo" || row.source === "generic") && typeof row.label === "string" && typeof row.draft === "string"
|
|
70
|
+
? [{ source: row.source, label: row.label.slice(0, 500), draft: row.draft.slice(0, 4000) }] : [];
|
|
71
|
+
});
|
|
72
|
+
}
|
package/dist/memory-pass.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* is the one writer, and it writes through the ordinary knowledge and
|
|
11
11
|
* decision stores so every edit keeps its revision.
|
|
12
12
|
*/
|
|
13
|
+
import { inEvalWorld } from './eval-gate.js';
|
|
13
14
|
import { createHash } from 'node:crypto';
|
|
14
15
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
15
16
|
import { homedir } from 'node:os';
|
|
@@ -188,6 +189,9 @@ export function analysisPrompt(surface, openGaps) {
|
|
|
188
189
|
/** The production analyser: one call through the configured chat provider, no tools, no history. */
|
|
189
190
|
export function defaultAnalyzer(store, options) {
|
|
190
191
|
return async (input) => {
|
|
192
|
+
// An eval world makes no provider call it can't record (eval-gate.ts, Codex round 1 #3): the backward pass waits.
|
|
193
|
+
if (inEvalWorld())
|
|
194
|
+
return { ok: false, problem: 'The backward pass makes no provider call in an eval world.' };
|
|
191
195
|
const config = store.getChatConfig();
|
|
192
196
|
if (config === null)
|
|
193
197
|
return { ok: false, problem: 'Chat is not configured; the backward pass uses the chat model.' };
|
package/dist/nightly-check.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export declare const NIGHTLY_ACCEPTANCE: readonly {
|
|
|
18
18
|
}[];
|
|
19
19
|
/** The ledger act that records a person's approval of a nightly check, keyed by its trigger. */
|
|
20
20
|
export declare const NIGHTLY_APPROVED = "nightly check approved";
|
|
21
|
+
/** The ledger act that records a person turning GitHub's nightly into release debt for a project (nightly-github.ts). */
|
|
22
|
+
export declare const GITHUB_NIGHTLY_ON = "github nightly debt on";
|
|
21
23
|
/** The digest a person's approval of the nightly signs: the trigger, the project, the schedule, the check-only terms
|
|
22
24
|
* and the fixed words each firing files. */
|
|
23
25
|
export declare function nightlyDigestOf(trigger: number, repo: string, schedule: string, check: MainCheck): string;
|
|
@@ -44,7 +46,8 @@ export declare const TRUSTED_MAIN_CLEAR_MS: number;
|
|
|
44
46
|
* last read failed, or when the read was of another remote than the one recorded now (review round 2).
|
|
45
47
|
*/
|
|
46
48
|
export declare function trustedMainOf(store: Store, repo: string, now: Date, maxAgeMs: number): string | null;
|
|
47
|
-
/** The watch loop's read of each project's recorded remote main that the nightly check
|
|
49
|
+
/** The watch loop's read of each project's recorded remote main that the nightly check, GitHub's nightly (its runs count
|
|
50
|
+
* only at commits on main's history) or main's debt needs, at most
|
|
48
51
|
* every 30 minutes. Each read is a fresh fetch into a private directory. A failed read forgets the last one. A good
|
|
49
52
|
* one hands its verified objects to the project's Git store (hardened git, content-addressed: git checks every object
|
|
50
53
|
* against its hash, and filing verifies the commit again), so the nightly can check a merge the checkout never pulled. */
|
package/dist/nightly-check.js
CHANGED
|
@@ -36,6 +36,8 @@ export const NIGHTLY_NOT = "Changing anything: this is a check only.";
|
|
|
36
36
|
export const NIGHTLY_ACCEPTANCE = Object.freeze([{ id: "c1", statement: "The full release check passes on main", how: null, evidence: ["check"] }]);
|
|
37
37
|
/** The ledger act that records a person's approval of a nightly check, keyed by its trigger. */
|
|
38
38
|
export const NIGHTLY_APPROVED = "nightly check approved";
|
|
39
|
+
/** The ledger act that records a person turning GitHub's nightly into release debt for a project (nightly-github.ts). */
|
|
40
|
+
export const GITHUB_NIGHTLY_ON = "github nightly debt on";
|
|
39
41
|
/** The digest a person's approval of the nightly signs: the trigger, the project, the schedule, the check-only terms
|
|
40
42
|
* and the fixed words each firing files. */
|
|
41
43
|
export function nightlyDigestOf(trigger, repo, schedule, check) {
|
|
@@ -91,14 +93,15 @@ export function trustedMainOf(store, repo, now, maxAgeMs) {
|
|
|
91
93
|
}
|
|
92
94
|
return sha;
|
|
93
95
|
}
|
|
94
|
-
/** The watch loop's read of each project's recorded remote main that the nightly check
|
|
96
|
+
/** The watch loop's read of each project's recorded remote main that the nightly check, GitHub's nightly (its runs count
|
|
97
|
+
* only at commits on main's history) or main's debt needs, at most
|
|
95
98
|
* every 30 minutes. Each read is a fresh fetch into a private directory. A failed read forgets the last one. A good
|
|
96
99
|
* one hands its verified objects to the project's Git store (hardened git, content-addressed: git checks every object
|
|
97
100
|
* against its hash, and filing verifies the commit again), so the nightly can check a merge the checkout never pulled. */
|
|
98
101
|
export async function refreshTrustedMains(store, root, now, runner = run) {
|
|
99
102
|
const repos = new Set([
|
|
100
103
|
...store.handle.prepare("SELECT DISTINCT repo FROM main_check_failure WHERE cleared_at IS NULL").all().map(row => String(row["repo"])),
|
|
101
|
-
...store.handle.prepare("SELECT DISTINCT repo FROM action_ledger WHERE action
|
|
104
|
+
...store.handle.prepare("SELECT DISTINCT repo FROM action_ledger WHERE action IN (?, ?) AND repo IS NOT NULL").all(NIGHTLY_APPROVED, GITHUB_NIGHTLY_ON).map(row => String(row["repo"])),
|
|
102
105
|
]);
|
|
103
106
|
let read = 0;
|
|
104
107
|
const remotes = new RemoteMains(store, runner, root);
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { GITHUB_NIGHTLY_ON } from "./nightly-check.js";
|
|
2
|
+
import { type Store } from "./store.js";
|
|
3
|
+
export { GITHUB_NIGHTLY_ON };
|
|
4
|
+
/** The one workflow file whose runs count: exactly this path, never one with an `@` suffix or another folder's. */
|
|
5
|
+
export declare const NIGHTLY_WORKFLOW_PATH = ".github/workflows/nightly.yml";
|
|
6
|
+
/** GitHub's re-run horizon: a counted run is read again for new attempts for this long. */
|
|
7
|
+
export declare const NIGHTLY_WATCH_MS: number;
|
|
8
|
+
/** Listing never goes further back than this: no run older than it can still be unfinished. */
|
|
9
|
+
export declare const NIGHTLY_LIST_HORIZON_MS: number;
|
|
10
|
+
/** Pages of runs one read lists at most; a longer backlog resumes on the next read. */
|
|
11
|
+
export declare const NIGHTLY_PAGES_PER_READ = 10;
|
|
12
|
+
/** A boundary's one bounded read (deploy, merge, pre-tag), and the background read's overall deadline. */
|
|
13
|
+
export declare const NIGHTLY_BOUNDARY_READ_MS = 20000;
|
|
14
|
+
export declare const NIGHTLY_BACKGROUND_READ_MS = 60000;
|
|
15
|
+
/** How often the watch loop reads each opted-in project's nightly. */
|
|
16
|
+
export declare const NIGHTLY_BACKGROUND_EVERY_MS: number;
|
|
17
|
+
export declare const SHARDS_PASSED_STEP = "Every shard passed";
|
|
18
|
+
export declare const SHARDS_COVERED_STEP = "Every shard ran the whole suite";
|
|
19
|
+
/** One GitHub REST read through the plane's own gh (release-adapters.ts githubReads), bounded by `timeoutMs`. */
|
|
20
|
+
export type GhRead = (path: string, timeoutMs: number) => Promise<{
|
|
21
|
+
ok: true;
|
|
22
|
+
body: unknown;
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
why: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Main as the plane read it from the recorded remote: whether a commit is on its first-parent history ("unknown" when
|
|
28
|
+
* main hasn't been read lately, the commit isn't here yet, or the read of main is older than `since` — a run created
|
|
29
|
+
* after main was read may be on a main that read hasn't seen), and whether one main commit contains another. */
|
|
30
|
+
export type MainHistory = {
|
|
31
|
+
/** `deadline`: wall-clock milliseconds by which the local history work stops ("unknown" then). */
|
|
32
|
+
onMain(sha: string, since: string, deadline: number): "yes" | "no" | "unknown";
|
|
33
|
+
/** Three-state, like onMain: "unknown" when history couldn't be read whole or the deadline passed. */
|
|
34
|
+
mainContains(sha: string, ancestor: string, deadline: number): "yes" | "no" | "unknown";
|
|
35
|
+
};
|
|
36
|
+
export type NightlyDeps = MainHistory & {
|
|
37
|
+
gh: GhRead;
|
|
38
|
+
now: () => Date;
|
|
39
|
+
};
|
|
40
|
+
/** One read inside a read's deadline. */
|
|
41
|
+
type Get = (path: string) => Promise<{
|
|
42
|
+
ok: true;
|
|
43
|
+
body: unknown;
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
why: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Whether a project opted in, as the plane recorded a person's step: the newest such ledger row, read strictly. */
|
|
49
|
+
export declare function githubNightlyOf(store: Store, repo: string): {
|
|
50
|
+
slug: string;
|
|
51
|
+
url: string;
|
|
52
|
+
record: number;
|
|
53
|
+
by: string;
|
|
54
|
+
at: string;
|
|
55
|
+
} | null;
|
|
56
|
+
/** Every project that ever opted in (a broken or superseded record still counts here: its debt still holds). */
|
|
57
|
+
export declare function githubNightlyRepos(store: Store): string[];
|
|
58
|
+
/** A lane's stable id: its workflow file and the required check's name when it was seeded. */
|
|
59
|
+
export declare const laneIdOf: (workflow: string, name: string) => string;
|
|
60
|
+
/** One lane per required check whose name no lane (by its name or an alias) answers to yet. A retired lane whose name
|
|
61
|
+
* is required again opens again. */
|
|
62
|
+
export declare function seedLanes(store: Store, repo: string, now: Date): void;
|
|
63
|
+
/** The live lane a job of `workflow` named `name` belongs to: a person's alias first (the newest for that name), then
|
|
64
|
+
* the lane seeded under it. Never a guess: anything else is no lane. */
|
|
65
|
+
export declare function resolveLane(store: Store, repo: string, workflow: string, name: string): string | null;
|
|
66
|
+
/** The name a lane is shown by: its newest alias, else the name it was seeded under. */
|
|
67
|
+
export declare function laneName(store: Store, repo: string, laneId: string): string;
|
|
68
|
+
/** `toolroll release nightly --github`: a person turns GitHub's nightly into release debt for this project. The
|
|
69
|
+
* repository is the project's recorded remote's, never the checkout's. */
|
|
70
|
+
export declare function optInGithubNightly(store: Store, input: {
|
|
71
|
+
repo: string;
|
|
72
|
+
as: string;
|
|
73
|
+
token: string;
|
|
74
|
+
}, now: Date): {
|
|
75
|
+
ok: true;
|
|
76
|
+
slug: string;
|
|
77
|
+
lanes: number;
|
|
78
|
+
} | {
|
|
79
|
+
ok: false;
|
|
80
|
+
message: string;
|
|
81
|
+
};
|
|
82
|
+
/** `--lane-alias <old-id> <new name>`: a person says a renamed job is the same lane. Open debt goes with the lane. */
|
|
83
|
+
export declare function aliasNightlyLane(store: Store, input: {
|
|
84
|
+
repo: string;
|
|
85
|
+
laneId: string;
|
|
86
|
+
name: string;
|
|
87
|
+
as: string;
|
|
88
|
+
token: string;
|
|
89
|
+
}, now: Date): {
|
|
90
|
+
ok: true;
|
|
91
|
+
} | {
|
|
92
|
+
ok: false;
|
|
93
|
+
message: string;
|
|
94
|
+
};
|
|
95
|
+
/** `--lane-retire <lane-id>`: a person retires a lane no required check names any more; its open debt closes,
|
|
96
|
+
* recorded as theirs. */
|
|
97
|
+
export declare function retireNightlyLane(store: Store, input: {
|
|
98
|
+
repo: string;
|
|
99
|
+
laneId: string;
|
|
100
|
+
as: string;
|
|
101
|
+
token: string;
|
|
102
|
+
}, now: Date): {
|
|
103
|
+
ok: true;
|
|
104
|
+
closed: number;
|
|
105
|
+
} | {
|
|
106
|
+
ok: false;
|
|
107
|
+
message: string;
|
|
108
|
+
};
|
|
109
|
+
type GhRun = {
|
|
110
|
+
id: number;
|
|
111
|
+
path: string;
|
|
112
|
+
repository: string | null;
|
|
113
|
+
headRepository: string | null;
|
|
114
|
+
headBranch: string | null;
|
|
115
|
+
headSha: string;
|
|
116
|
+
event: string;
|
|
117
|
+
status: string;
|
|
118
|
+
runAttempt: number;
|
|
119
|
+
createdAt: string;
|
|
120
|
+
};
|
|
121
|
+
type GhStep = {
|
|
122
|
+
number: number;
|
|
123
|
+
name: string;
|
|
124
|
+
status: string;
|
|
125
|
+
conclusion: string | null;
|
|
126
|
+
};
|
|
127
|
+
export type GhJob = {
|
|
128
|
+
id: number;
|
|
129
|
+
name: string;
|
|
130
|
+
status: string;
|
|
131
|
+
conclusion: string | null;
|
|
132
|
+
startedAt: string | null;
|
|
133
|
+
completedAt: string | null;
|
|
134
|
+
headSha: string;
|
|
135
|
+
runId: number;
|
|
136
|
+
runAttempt: number | null;
|
|
137
|
+
steps: GhStep[] | null;
|
|
138
|
+
};
|
|
139
|
+
/** Whether a run is one of the project's own nightly runs on main (everything but main's history, checked apart). */
|
|
140
|
+
export declare function nightlyRunBinds(run: Pick<GhRun, "path" | "repository" | "headRepository" | "headBranch" | "event" | "headSha">, slug: string): boolean;
|
|
141
|
+
export type Cause = "passed" | "test-failure" | "runner-failure" | "not-run";
|
|
142
|
+
/**
|
|
143
|
+
* Why a completed lane job ended as it did, from GitHub's own job and step records in the same attempt
|
|
144
|
+
* (evals-nightly.md, "Cause of a failure"). Fails closed: anything not positively the runner's is a test failure.
|
|
145
|
+
*/
|
|
146
|
+
export declare function causeOf(job: GhJob, attempt: readonly GhJob[]): Cause;
|
|
147
|
+
export type NightlyRead = {
|
|
148
|
+
ok: true;
|
|
149
|
+
listed: number;
|
|
150
|
+
checked: number;
|
|
151
|
+
complete: boolean;
|
|
152
|
+
} | {
|
|
153
|
+
ok: false;
|
|
154
|
+
why: string;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* One read of the project's GitHub nightly within `budgetMs`: list runs from the cursor, re-read the watch list (new
|
|
158
|
+
* and unfinished runs first, then the oldest-checked), record every completed lane job, open and clear failures. A
|
|
159
|
+
* failed call stops where it is; what was recorded stays, and the cursor never moves past a page it couldn't read.
|
|
160
|
+
*/
|
|
161
|
+
export declare function readGithubNightly(store: Store, repo: string, deps: NightlyDeps, budgetMs: number): Promise<NightlyRead>;
|
|
162
|
+
/** Every job of one exact attempt of one run, every page, each GitHub's record of that run, attempt and commit. Null
|
|
163
|
+
* when any page can't be read whole. */
|
|
164
|
+
export declare function readAttemptJobs(get: Get, slug: string, run: number, attempt: number, sha: string): Promise<GhJob[] | null>;
|
|
165
|
+
/** A recorded open failure of a lane. */
|
|
166
|
+
export type LaneFailure = {
|
|
167
|
+
id: number;
|
|
168
|
+
repo: string;
|
|
169
|
+
laneId: string;
|
|
170
|
+
ghRun: number;
|
|
171
|
+
attempt: number;
|
|
172
|
+
jobId: number;
|
|
173
|
+
sha: string;
|
|
174
|
+
completedAt: string;
|
|
175
|
+
openedAt: string;
|
|
176
|
+
};
|
|
177
|
+
export declare function openFailures(store: Store, repo: string): LaneFailure[];
|
|
178
|
+
/**
|
|
179
|
+
* Clear each open failure that a later counted nightly run passed: a run created after the failed run (a rerun of an
|
|
180
|
+
* older run never clears newer debt; Codex round 1, finding 2), the same lane, a job started after the failed one
|
|
181
|
+
* completed, on a commit that contains the failed one. A re-run inside the same run never clears. Local history work
|
|
182
|
+
* stops at `wall` (wall-clock ms); what is left is settled on a later read.
|
|
183
|
+
*/
|
|
184
|
+
export declare function settleGithubNightly(store: Store, repo: string, history: Pick<MainHistory, "mainContains">, now: Date, wall?: number): {
|
|
185
|
+
cleared: number;
|
|
186
|
+
complete: boolean;
|
|
187
|
+
unresolved: string | null;
|
|
188
|
+
};
|
|
189
|
+
/** Main's history as the plane read it from the project's recorded remote (nightly-check.ts trustedMainOf), from the
|
|
190
|
+
* verified objects in the project's own Git store; never a ref a build could move. */
|
|
191
|
+
export declare function mainHistoryOf(store: Store, repo: string, now: Date): MainHistory;
|
|
192
|
+
/**
|
|
193
|
+
* The watch loop's background read (operate.ts refreshGithubNightlies): each opted-in project at most every
|
|
194
|
+
* NIGHTLY_BACKGROUND_EVERY_MS, claimed in the store so two processes don't both read, within one overall deadline.
|
|
195
|
+
*/
|
|
196
|
+
export declare function readGithubNightlies(store: Store, deps: {
|
|
197
|
+
gh: GhRead;
|
|
198
|
+
now: () => Date;
|
|
199
|
+
history?: (repo: string, now: Date) => MainHistory;
|
|
200
|
+
}, budgetMs?: number): Promise<number>;
|
|
201
|
+
/** What a hold stops: a deploy, the merge, the tag (pre-tag authorization), or an automatic approval (sealAutoGreen). */
|
|
202
|
+
export type NightlyPhase = "deploy" | "merge" | "tag" | "auto";
|
|
203
|
+
/** How long a boundary's re-read of a candidate's exemption stands for the stored checks inside the reservation, the
|
|
204
|
+
* deployment gate and the installed runtime's deploy check after it. */
|
|
205
|
+
export declare const EXEMPTION_FRESH_MS: number;
|
|
206
|
+
/** "Oct 30, 06:17 UTC". */
|
|
207
|
+
export declare const nightlyStamp: (iso: string) => string;
|
|
208
|
+
/** The words of a nightly hold: the first open failure, how many more, and the way out at this point. */
|
|
209
|
+
export declare function nightlyDebtWords(store: Store, held: readonly LaneFailure[], phase: NightlyPhase, release: boolean): string;
|
|
210
|
+
/**
|
|
211
|
+
* The stored check every boundary makes, inside the caller's own transaction where it has one (the deployment gate, the
|
|
212
|
+
* deploy and merge reservations, the pre-tag step, an automatic approval): the project's open lane failures, less those
|
|
213
|
+
* this exact task and candidate hold a fresh exemption for. Null when nothing holds. A project that never opted in has
|
|
214
|
+
* no lane failures.
|
|
215
|
+
*/
|
|
216
|
+
export declare function nightlyDebtHold(store: Store, input: {
|
|
217
|
+
repo: string;
|
|
218
|
+
taskId: string | null;
|
|
219
|
+
candidate: string | null;
|
|
220
|
+
phase: NightlyPhase;
|
|
221
|
+
release: boolean;
|
|
222
|
+
now: Date;
|
|
223
|
+
}): {
|
|
224
|
+
why: string;
|
|
225
|
+
failures: LaneFailure[];
|
|
226
|
+
} | null;
|
|
227
|
+
/** How the project's GitHub nightly stands: whether it is on, when it was last read (and why not), when it last ran. */
|
|
228
|
+
export type NightlyStatus = {
|
|
229
|
+
on: boolean;
|
|
230
|
+
lastOkAt: string | null;
|
|
231
|
+
lastWhy: string | null;
|
|
232
|
+
lastRunAt: string | null;
|
|
233
|
+
stale: boolean;
|
|
234
|
+
};
|
|
235
|
+
/** No nightly run seen for this long reads "hasn't run since". */
|
|
236
|
+
export declare const NIGHTLY_STALE_MS: number;
|
|
237
|
+
export declare function githubNightlyStatus(store: Store, repo: string, now: Date): NightlyStatus;
|
|
238
|
+
/** A warning that never holds anything (Decision 1): GitHub couldn't be read, or the nightly hasn't run lately. */
|
|
239
|
+
export declare function nightlyWarning(status: NightlyStatus, now: Date): string | null;
|
|
240
|
+
/** One workflow's newest pull-request run on a candidate head, as an exemption reads it. */
|
|
241
|
+
export type LaneJobs = {
|
|
242
|
+
run: number;
|
|
243
|
+
attempt: number;
|
|
244
|
+
status: string;
|
|
245
|
+
jobs: GhJob[];
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* A release candidate's own pull-request CI, as an exemption needs it (modelled on scripts/release-proof.mjs): for each
|
|
249
|
+
* workflow, the newest run on `head` from that pull request's `pull_request` event, that run's latest attempt's jobs
|
|
250
|
+
* with their times, then the run read again. The newest run is chosen first: one that doesn't bind to this pull
|
|
251
|
+
* request never lets an older run stand. `null` for a workflow with no such run; "unreadable" when anything can't be
|
|
252
|
+
* read whole or changed during the read.
|
|
253
|
+
*/
|
|
254
|
+
export declare function readLaneJobs(get: Get, slug: string, pr: number, head: string, workflows: readonly string[]): Promise<Map<string, LaneJobs | null> | "unreadable">;
|
|
255
|
+
/** The files a lane's job runs that the candidate could otherwise rewrite to make a job of that name pass: its workflow
|
|
256
|
+
* file and the CI scripts the workflows call. An exemption stands only when each is byte-identical (the same Git
|
|
257
|
+
* blob) on the candidate and at the failed commit. The tests themselves are the candidate's, as for every required
|
|
258
|
+
* check (the trust the required-checks gate already places in them). */
|
|
259
|
+
export declare const LANE_PINNED_FILES: readonly ["scripts/ci-shards.mjs", "scripts/ci-scope.mjs", "scripts/ci-git.sh", "scripts/ci-no-skips.mjs"];
|
|
260
|
+
export type NightlyBoundary = {
|
|
261
|
+
ok: true;
|
|
262
|
+
warning: string | null;
|
|
263
|
+
} | {
|
|
264
|
+
ok: false;
|
|
265
|
+
why: string;
|
|
266
|
+
warning: string | null;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* A boundary (the coordinator's deploy, merge and pre-tag steps): one bounded read of GitHub's nightly, then this
|
|
270
|
+
* candidate's exemptions read again from its own pull-request CI — minted only before the merge (`deploy`, `merge`);
|
|
271
|
+
* at the tag an exemption obtained before the merge is re-read, never made — then the stored debt. A read that fails is
|
|
272
|
+
* a warning and the stored debt still holds; an exemption that can't be re-read is no exemption here.
|
|
273
|
+
*/
|
|
274
|
+
export declare function nightlyBoundary(store: Store, input: {
|
|
275
|
+
repo: string;
|
|
276
|
+
taskId: string;
|
|
277
|
+
candidate: string;
|
|
278
|
+
pr: number;
|
|
279
|
+
phase: Exclude<NightlyPhase, "auto">;
|
|
280
|
+
}, deps: NightlyDeps, budgetMs?: number): Promise<NightlyBoundary>;
|
|
281
|
+
/** One project-level Needs-you item: an open GitHub nightly lane failure, or a nightly check of main that failed. */
|
|
282
|
+
export type NightlyNeed = {
|
|
283
|
+
key: string;
|
|
284
|
+
repo: string;
|
|
285
|
+
title: string;
|
|
286
|
+
reason: string;
|
|
287
|
+
href: string;
|
|
288
|
+
since: string;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Needs you, for the projects `shown` admits: one item per open GitHub nightly lane failure (linking to its job on
|
|
292
|
+
* GitHub, under the opted-in repository the plane recorded), and one per nightly check of main with open failures
|
|
293
|
+
* (linking to its task). Oldest first.
|
|
294
|
+
*/
|
|
295
|
+
export declare function nightlyNeedsYou(store: Store, shown: (repo: string) => boolean): NightlyNeed[];
|
|
296
|
+
/** The release card's GitHub nightly lines: each open lane failure (held, or exempted for this candidate), then a
|
|
297
|
+
* warning when GitHub couldn't be read or the nightly hasn't run lately. Empty when there is nothing to say. */
|
|
298
|
+
export declare function nightlyReleaseLines(store: Store, input: {
|
|
299
|
+
repo: string;
|
|
300
|
+
taskId: string;
|
|
301
|
+
candidate: string | null;
|
|
302
|
+
now: Date;
|
|
303
|
+
}): string[];
|
|
304
|
+
/**
|
|
305
|
+
* A deploy run's own boundary (scripts/deploy-browser.mjs, through the installed runtime; Codex round 1, finding 3):
|
|
306
|
+
* the same bounded read as the coordinator's — GitHub's nightly, then this candidate's exemptions re-read — before the
|
|
307
|
+
* stored check, so a deploy started by hand neither misses a new failure nor rides an exemption its CI no longer
|
|
308
|
+
* supports. A task no release covers has no exemption to re-read; its nightly is still read. Anything that fails here
|
|
309
|
+
* takes away the candidate's stored exemptions; the stored check that follows decides.
|
|
310
|
+
*/
|
|
311
|
+
export declare function refreshNightlyForDeploy(store: Store, input: {
|
|
312
|
+
runId: number;
|
|
313
|
+
now: () => Date;
|
|
314
|
+
gh?: GhRead;
|
|
315
|
+
budgetMs?: number;
|
|
316
|
+
}): Promise<NightlyBoundary | null>;
|