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.
Files changed (168) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/cli.js +1 -0
  3. package/dist/commit-extract.d.ts +18 -0
  4. package/dist/commit-extract.js +39 -1
  5. package/dist/converse.d.ts +12 -0
  6. package/dist/converse.js +18 -2
  7. package/dist/deploy-gate.js +8 -0
  8. package/dist/desktop-update.d.ts +1 -1
  9. package/dist/eval-gate.d.ts +51 -0
  10. package/dist/eval-gate.js +199 -0
  11. package/dist/eval-hooks.d.ts +39 -0
  12. package/dist/eval-hooks.js +477 -0
  13. package/dist/eval-oracle-main.d.ts +1 -0
  14. package/dist/eval-oracle-main.js +31 -0
  15. package/dist/eval-oracle.d.ts +60 -0
  16. package/dist/eval-oracle.js +217 -0
  17. package/dist/eval-world.d.ts +17 -0
  18. package/dist/eval-world.js +97 -0
  19. package/dist/evals.d.ts +299 -0
  20. package/dist/evals.js +964 -0
  21. package/dist/exec.js +15 -1
  22. package/dist/first-tasks-child.d.ts +1 -0
  23. package/dist/first-tasks-child.js +11 -0
  24. package/dist/flow-draft.js +4 -0
  25. package/dist/flow-sort.js +4 -0
  26. package/dist/folder-look.d.ts +107 -0
  27. package/dist/folder-look.js +302 -0
  28. package/dist/github-jobs.d.ts +25 -0
  29. package/dist/github-jobs.js +22 -0
  30. package/dist/invoke.d.ts +1 -1
  31. package/dist/invoke.js +14 -0
  32. package/dist/lead.js +4 -1
  33. package/dist/learning-child.d.ts +1 -0
  34. package/dist/learning-child.js +34 -0
  35. package/dist/learning-look.d.ts +25 -0
  36. package/dist/learning-look.js +72 -0
  37. package/dist/memory-pass.js +4 -0
  38. package/dist/nightly-check.d.ts +4 -1
  39. package/dist/nightly-check.js +5 -2
  40. package/dist/nightly-github.d.ts +316 -0
  41. package/dist/nightly-github.js +1022 -0
  42. package/dist/operate.d.ts +123 -1
  43. package/dist/operate.js +344 -29
  44. package/dist/project-identity.d.ts +37 -0
  45. package/dist/project-identity.js +106 -0
  46. package/dist/project-knowledge.d.ts +4 -4
  47. package/dist/project-tools.d.ts +4 -1
  48. package/dist/project-tools.js +7 -2
  49. package/dist/project.d.ts +4 -1
  50. package/dist/project.js +11 -4
  51. package/dist/provider.d.ts +3 -0
  52. package/dist/provider.js +9 -2
  53. package/dist/release-adapters.d.ts +21 -21
  54. package/dist/release-adapters.js +52 -19
  55. package/dist/release-auto.js +26 -0
  56. package/dist/release-card.js +3 -0
  57. package/dist/release-cli.js +59 -3
  58. package/dist/release-view.d.ts +3 -0
  59. package/dist/release-view.js +2 -0
  60. package/dist/release.d.ts +16 -0
  61. package/dist/release.js +16 -0
  62. package/dist/reported-usage.d.ts +69 -0
  63. package/dist/reported-usage.js +97 -0
  64. package/dist/repos.d.ts +7 -0
  65. package/dist/repos.js +57 -0
  66. package/dist/serve.js +35 -8
  67. package/dist/server/chat.js +4 -0
  68. package/dist/server/flows.js +9 -3
  69. package/dist/server/guards.d.ts +4 -0
  70. package/dist/server/guards.js +12 -3
  71. package/dist/server/http.d.ts +11 -0
  72. package/dist/server/http.js +3 -0
  73. package/dist/server/pages.js +62 -88
  74. package/dist/server/render-pages.d.ts +2 -16
  75. package/dist/server/render-pages.js +1 -55
  76. package/dist/server/render-tasks.js +1 -0
  77. package/dist/server/runtime.d.ts +4 -0
  78. package/dist/server/runtime.js +1 -0
  79. package/dist/server/settings.js +14 -3
  80. package/dist/server/tasks.js +15 -4
  81. package/dist/store.d.ts +109 -1
  82. package/dist/store.js +394 -4
  83. package/dist/subagents.js +4 -0
  84. package/dist/subscription-chat.d.ts +2 -0
  85. package/dist/subscription-chat.js +52 -13
  86. package/dist/task-sizing.js +4 -0
  87. package/dist/teardown-git.d.ts +9 -2
  88. package/dist/teardown-git.js +56 -33
  89. package/dist/up-processes.d.ts +13 -0
  90. package/dist/up-processes.js +6 -2
  91. package/evals/cases/catalog/case.json +25 -0
  92. package/evals/cases/catalog/fixture/README.md +3 -0
  93. package/evals/cases/catalog/fixture/package.json +8 -0
  94. package/evals/cases/catalog/fixture/test/catalog.test.mjs +4 -0
  95. package/evals/cases/catalog/oracle.mjs +23 -0
  96. package/evals/cases/catalog/probe.mjs +10 -0
  97. package/evals/cases/catalog/source.md +9 -0
  98. package/evals/cases/csv-quoting/case.json +23 -0
  99. package/evals/cases/csv-quoting/fixture/README.md +3 -0
  100. package/evals/cases/csv-quoting/fixture/csv.js +1 -0
  101. package/evals/cases/csv-quoting/fixture/package.json +8 -0
  102. package/evals/cases/csv-quoting/fixture/test/csv.test.mjs +5 -0
  103. package/evals/cases/csv-quoting/oracle.mjs +14 -0
  104. package/evals/cases/csv-quoting/probe.mjs +9 -0
  105. package/evals/cases/csv-quoting/source.md +9 -0
  106. package/evals/cases/dedupe/case.json +23 -0
  107. package/evals/cases/dedupe/fixture/README.md +3 -0
  108. package/evals/cases/dedupe/fixture/dedupe.js +1 -0
  109. package/evals/cases/dedupe/fixture/package.json +8 -0
  110. package/evals/cases/dedupe/fixture/test/dedupe.test.mjs +5 -0
  111. package/evals/cases/dedupe/oracle.mjs +16 -0
  112. package/evals/cases/dedupe/probe.mjs +13 -0
  113. package/evals/cases/dedupe/source.md +9 -0
  114. package/evals/cases/endpoint/case.json +25 -0
  115. package/evals/cases/endpoint/fixture/README.md +3 -0
  116. package/evals/cases/endpoint/fixture/package.json +8 -0
  117. package/evals/cases/endpoint/fixture/test/endpoint.test.mjs +4 -0
  118. package/evals/cases/endpoint/oracle.mjs +27 -0
  119. package/evals/cases/endpoint/probe.mjs +10 -0
  120. package/evals/cases/endpoint/source.md +11 -0
  121. package/evals/cases/math-divide/case.json +23 -0
  122. package/evals/cases/math-divide/fixture/README.md +3 -0
  123. package/evals/cases/math-divide/fixture/package.json +8 -0
  124. package/evals/cases/math-divide/fixture/src/math.js +1 -0
  125. package/evals/cases/math-divide/fixture/test/math.test.js +5 -0
  126. package/evals/cases/math-divide/oracle.mjs +13 -0
  127. package/evals/cases/math-divide/probe.mjs +9 -0
  128. package/evals/cases/math-divide/source.md +9 -0
  129. package/evals/cases/math-multiply/case.json +23 -0
  130. package/evals/cases/math-multiply/fixture/README.md +3 -0
  131. package/evals/cases/math-multiply/fixture/package.json +8 -0
  132. package/evals/cases/math-multiply/fixture/src/math.js +2 -0
  133. package/evals/cases/math-multiply/fixture/test/math.test.js +5 -0
  134. package/evals/cases/math-multiply/oracle.mjs +12 -0
  135. package/evals/cases/math-multiply/probe.mjs +9 -0
  136. package/evals/cases/math-multiply/source.md +9 -0
  137. package/evals/cases/math-subtract/case.json +23 -0
  138. package/evals/cases/math-subtract/fixture/README.md +3 -0
  139. package/evals/cases/math-subtract/fixture/package.json +8 -0
  140. package/evals/cases/math-subtract/fixture/src/math.js +1 -0
  141. package/evals/cases/math-subtract/fixture/test/math.test.js +5 -0
  142. package/evals/cases/math-subtract/oracle.mjs +12 -0
  143. package/evals/cases/math-subtract/probe.mjs +9 -0
  144. package/evals/cases/math-subtract/source.md +9 -0
  145. package/evals/cases/merge-config/case.json +23 -0
  146. package/evals/cases/merge-config/fixture/README.md +3 -0
  147. package/evals/cases/merge-config/fixture/config.js +1 -0
  148. package/evals/cases/merge-config/fixture/package.json +8 -0
  149. package/evals/cases/merge-config/fixture/test/config.test.mjs +5 -0
  150. package/evals/cases/merge-config/oracle.mjs +28 -0
  151. package/evals/cases/merge-config/probe.mjs +15 -0
  152. package/evals/cases/merge-config/source.md +9 -0
  153. package/evals/cases/paginate/case.json +23 -0
  154. package/evals/cases/paginate/fixture/README.md +3 -0
  155. package/evals/cases/paginate/fixture/package.json +8 -0
  156. package/evals/cases/paginate/fixture/paginate.js +1 -0
  157. package/evals/cases/paginate/fixture/test/paginate.test.mjs +5 -0
  158. package/evals/cases/paginate/oracle.mjs +18 -0
  159. package/evals/cases/paginate/probe.mjs +13 -0
  160. package/evals/cases/paginate/source.md +9 -0
  161. package/evals/cases/round2-asks/case.json +24 -0
  162. package/evals/cases/round2-asks/fixture/README.md +3 -0
  163. package/evals/cases/round2-asks/fixture/package.json +8 -0
  164. package/evals/cases/round2-asks/fixture/src/math.js +1 -0
  165. package/evals/cases/round2-asks/fixture/test/math.test.js +5 -0
  166. package/evals/cases/round2-asks/source.md +11 -0
  167. package/evals/manifest.json +15 -0
  168. 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
+ }
@@ -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.' };
@@ -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 or main's debt needs, at most
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. */
@@ -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 or main's debt needs, at most
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 = ?").all(NIGHTLY_APPROVED).map(row => String(row["repo"])),
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>;