toolroll 0.9.65 → 0.9.68

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 (264) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +20 -2
  3. package/dist/acp-client.js +1 -3
  4. package/dist/agent-board.d.ts +0 -1
  5. package/dist/agent-board.js +0 -1
  6. package/dist/agent-fence.d.ts +69 -2
  7. package/dist/agent-fence.js +294 -11
  8. package/dist/agentconfig.js +4 -1
  9. package/dist/batch-checks.js +3 -2
  10. package/dist/browser/THIRD_PARTY_NOTICES.txt +46 -0
  11. package/dist/browser/terminal.js +134 -23
  12. package/dist/browser/workspace.css +2 -2
  13. package/dist/browser/workspace.js +31 -29
  14. package/dist/builder.d.ts +4 -0
  15. package/dist/builder.js +56 -37
  16. package/dist/canonical-json.d.ts +14 -0
  17. package/dist/canonical-json.js +36 -0
  18. package/dist/chat-actions.js +1 -2
  19. package/dist/chat-admission.d.ts +19 -0
  20. package/dist/chat-admission.js +22 -0
  21. package/dist/chat-channel.js +1 -1
  22. package/dist/chat-delivery.js +0 -1
  23. package/dist/chat-follow.d.ts +17 -0
  24. package/dist/chat-follow.js +32 -0
  25. package/dist/chat-messages.js +0 -1
  26. package/dist/chat-settings-page.d.ts +23 -0
  27. package/dist/chat-settings-page.js +41 -0
  28. package/dist/claim.js +3 -6
  29. package/dist/claude-print.d.ts +12 -0
  30. package/dist/claude-print.js +34 -0
  31. package/dist/cli-http.d.ts +0 -7
  32. package/dist/cli-http.js +0 -21
  33. package/dist/cli.d.ts +3 -10
  34. package/dist/cli.js +15 -24
  35. package/dist/coding-provider.js +2 -1
  36. package/dist/coding-workspace.js +1 -1
  37. package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
  38. package/dist/connect-cli.js +331 -0
  39. package/dist/containment.d.ts +0 -2
  40. package/dist/containment.js +2 -6
  41. package/dist/contracts/chat-actions.d.ts +0 -1
  42. package/dist/contracts/chat-content.d.ts +0 -4
  43. package/dist/contracts/chat-content.js +0 -2
  44. package/dist/contracts/chat-tables.d.ts +0 -1
  45. package/dist/contracts/cli.d.ts +0 -62
  46. package/dist/contracts/cli.js +0 -4
  47. package/dist/contracts/coding-workspace.js +1 -1
  48. package/dist/contracts/console-api.d.ts +1 -0
  49. package/dist/contracts/console-api.js +1 -0
  50. package/dist/contracts/contract.js +1 -1
  51. package/dist/contracts/flow-card.d.ts +0 -1
  52. package/dist/contracts/flow-propose.d.ts +0 -1
  53. package/dist/contracts/flow-send.js +1 -1
  54. package/dist/contracts/gateway-tools.d.ts +0 -1
  55. package/dist/contracts/integration-metadata.js +1 -1
  56. package/dist/contracts/lead-tools.d.ts +0 -1
  57. package/dist/contracts/memory-pass.d.ts +0 -1
  58. package/dist/contracts/memory-pass.js +1 -1
  59. package/dist/contracts/plan.js +1 -1
  60. package/dist/contracts/project-knowledge.js +1 -1
  61. package/dist/contracts/project-skills.d.ts +0 -1
  62. package/dist/contracts/proof.js +1 -1
  63. package/dist/contracts/recipes.js +1 -1
  64. package/dist/contracts/scope.js +1 -1
  65. package/dist/contracts/scout-report.js +1 -1
  66. package/dist/contracts/teams-callback.d.ts +0 -1
  67. package/dist/converse.d.ts +0 -6
  68. package/dist/converse.js +0 -19
  69. package/dist/daemon.js +3 -2
  70. package/dist/decision.d.ts +1 -3
  71. package/dist/deprecations.d.ts +12 -4
  72. package/dist/deprecations.js +27 -4
  73. package/dist/desktop-update-recovery.js +1 -1
  74. package/dist/desktop-update.js +3 -2
  75. package/dist/discord-chat.js +4 -15
  76. package/dist/discord-settings.d.ts +2 -6
  77. package/dist/discord-settings.js +10 -38
  78. package/dist/discord.js +12 -23
  79. package/dist/evidence.d.ts +0 -1
  80. package/dist/evidence.js +0 -3
  81. package/dist/exec.d.ts +0 -9
  82. package/dist/exec.js +3 -81
  83. package/dist/flow-actions.js +0 -3
  84. package/dist/flow-draft.d.ts +1 -3
  85. package/dist/flow-draft.js +15 -28
  86. package/dist/flow-scripts.d.ts +1 -8
  87. package/dist/flow-scripts.js +0 -13
  88. package/dist/flow-send.js +1 -1
  89. package/dist/flow-share.d.ts +0 -2
  90. package/dist/flow-share.js +1 -6
  91. package/dist/flow-steps.js +1 -1
  92. package/dist/flow-triggers.d.ts +0 -1
  93. package/dist/flow-triggers.js +1 -2
  94. package/dist/flow-unattended.js +1 -12
  95. package/dist/flows.js +1 -1
  96. package/dist/health-cli.d.ts +2 -2
  97. package/dist/health-cli.js +1 -1
  98. package/dist/invoke.d.ts +18 -3
  99. package/dist/invoke.js +142 -17
  100. package/dist/is-record.d.ts +2 -0
  101. package/dist/is-record.js +2 -0
  102. package/dist/lead-doors.d.ts +1 -5
  103. package/dist/lead-doors.js +0 -4
  104. package/dist/lead-tools.js +3 -6
  105. package/dist/live-detail.d.ts +0 -1
  106. package/dist/live-detail.js +0 -6
  107. package/dist/live-terminal.d.ts +24 -13
  108. package/dist/live-terminal.js +81 -43
  109. package/dist/live.d.ts +0 -1
  110. package/dist/live.js +1 -2
  111. package/dist/mcp-core.js +0 -2
  112. package/dist/memory-pass.js +1 -1
  113. package/dist/model-catalog.js +1 -1
  114. package/dist/modes.js +1 -12
  115. package/dist/operate.d.ts +2 -0
  116. package/dist/operate.js +436 -745
  117. package/dist/password-bearer.d.ts +0 -2
  118. package/dist/password-bearer.js +0 -4
  119. package/dist/peek-cli.js +2 -1
  120. package/dist/peek.d.ts +2 -26
  121. package/dist/peek.js +2 -168
  122. package/dist/phase-routing.d.ts +0 -4
  123. package/dist/phase-routing.js +2 -21
  124. package/dist/planner-source.d.ts +2 -1
  125. package/dist/planner-source.js +2 -12
  126. package/dist/planner.js +2 -14
  127. package/dist/process-tree.d.ts +0 -2
  128. package/dist/process-tree.js +0 -10
  129. package/dist/project-delete-ui.d.ts +6 -0
  130. package/dist/project-delete-ui.js +20 -1
  131. package/dist/project-delete.d.ts +0 -2
  132. package/dist/project-delete.js +1 -3
  133. package/dist/project-learning.d.ts +0 -14
  134. package/dist/project-skills.d.ts +0 -1
  135. package/dist/project-skills.js +0 -14
  136. package/dist/project-tools.js +2 -10
  137. package/dist/provider.d.ts +0 -17
  138. package/dist/provider.js +0 -26
  139. package/dist/pulls.d.ts +0 -2
  140. package/dist/pulls.js +0 -9
  141. package/dist/release-adapters.d.ts +9 -0
  142. package/dist/release-adapters.js +81 -4
  143. package/dist/release.d.ts +7 -1
  144. package/dist/release.js +20 -7
  145. package/dist/remote-exec.d.ts +3 -2
  146. package/dist/remote-exec.js +3 -2
  147. package/dist/repos.d.ts +0 -5
  148. package/dist/repos.js +2 -10
  149. package/dist/repository-context.d.ts +0 -2
  150. package/dist/repository-context.js +0 -4
  151. package/dist/restart-certification.d.ts +0 -2
  152. package/dist/restart-certification.js +1 -5
  153. package/dist/result-follow-ups.d.ts +1 -4
  154. package/dist/result-follow-ups.js +4 -8
  155. package/dist/result-review.d.ts +0 -2
  156. package/dist/result-review.js +0 -2
  157. package/dist/retention.d.ts +0 -1
  158. package/dist/retention.js +0 -1
  159. package/dist/review-context.d.ts +0 -1
  160. package/dist/review-context.js +1 -12
  161. package/dist/reviewer.d.ts +0 -16
  162. package/dist/reviewer.js +0 -18
  163. package/dist/run-closeout.js +2 -1
  164. package/dist/scope.d.ts +13 -0
  165. package/dist/scope.js +11 -18
  166. package/dist/scout-browser.js +2 -1
  167. package/dist/serve.js +3 -2
  168. package/dist/server/chrome.d.ts +10 -1
  169. package/dist/server/chrome.js +45 -4
  170. package/dist/server/console.css +60 -137
  171. package/dist/server/flows.js +2 -2
  172. package/dist/server/http.d.ts +2 -0
  173. package/dist/server/http.js +5 -0
  174. package/dist/server/pages.js +2 -2
  175. package/dist/server/people-tokens.js +3 -4
  176. package/dist/server/remote.js +1 -6
  177. package/dist/server/render-chat.d.ts +9 -2
  178. package/dist/server/render-chat.js +53 -7
  179. package/dist/server/render-pages.d.ts +28 -3
  180. package/dist/server/render-pages.js +78 -9
  181. package/dist/server/render-results.d.ts +0 -6
  182. package/dist/server/render-results.js +3 -9
  183. package/dist/server/render-tasks.js +6 -6
  184. package/dist/server/route-table.d.ts +0 -4
  185. package/dist/server/route-table.js +1 -6
  186. package/dist/server/settings.js +43 -22
  187. package/dist/server/task-views.js +1 -1
  188. package/dist/server/tasks.js +10 -7
  189. package/dist/setup-guide.d.ts +2 -2
  190. package/dist/skills.d.ts +0 -6
  191. package/dist/skills.js +0 -8
  192. package/dist/slack-chat.d.ts +1 -7
  193. package/dist/slack-chat.js +5 -18
  194. package/dist/slack-settings.d.ts +2 -6
  195. package/dist/slack-settings.js +10 -38
  196. package/dist/slack.js +12 -23
  197. package/dist/sleep.d.ts +2 -0
  198. package/dist/sleep.js +2 -0
  199. package/dist/store.d.ts +11 -74
  200. package/dist/store.js +36 -179
  201. package/dist/structured-output.d.ts +0 -1
  202. package/dist/structured-output.js +0 -1
  203. package/dist/subagent-work.d.ts +0 -2
  204. package/dist/subagent-work.js +0 -2
  205. package/dist/subagents.d.ts +1 -3
  206. package/dist/subagents.js +20 -33
  207. package/dist/subscription-chat.d.ts +1 -3
  208. package/dist/subscription-chat.js +2 -3
  209. package/dist/task-sizing.d.ts +1 -4
  210. package/dist/task-sizing.js +3 -10
  211. package/dist/teams-chat.js +4 -11
  212. package/dist/teams.js +12 -19
  213. package/dist/teardown-git.d.ts +22 -0
  214. package/dist/teardown-git.js +136 -2
  215. package/dist/telegram-lead.js +2 -2
  216. package/dist/telegram.js +1 -1
  217. package/dist/token-notices.d.ts +0 -11
  218. package/dist/token-notices.js +1 -5
  219. package/dist/tool-broker.d.ts +12 -0
  220. package/dist/tool-broker.js +66 -2
  221. package/dist/tool-secret-store.d.ts +0 -3
  222. package/dist/tool-secret-store.js +2 -11
  223. package/dist/toolroll-update.d.ts +0 -2
  224. package/dist/toolroll-update.js +2 -2
  225. package/dist/unfenced-test-support.d.ts +2 -0
  226. package/dist/unfenced-test-support.js +11 -0
  227. package/dist/verification-evidence.d.ts +0 -1
  228. package/dist/verification-evidence.js +0 -1
  229. package/dist/webhooks.d.ts +0 -1
  230. package/dist/webhooks.js +0 -1
  231. package/dist/workspace-ui.d.ts +0 -8
  232. package/dist/workspace-ui.js +1 -6
  233. package/dist/writes-mode.d.ts +9 -0
  234. package/dist/writes-mode.js +8 -0
  235. package/package.json +5 -1
  236. package/dist/contracts/native-census.d.ts +0 -42
  237. package/dist/contracts/native-census.js +0 -36
  238. package/dist/process-recovery-anchor.d.ts +0 -75
  239. package/dist/process-recovery-anchor.js +0 -188
  240. package/dist/process-recovery-coalition.d.ts +0 -25
  241. package/dist/process-recovery-coalition.js +0 -105
  242. package/dist/process-recovery-eligibility.d.ts +0 -179
  243. package/dist/process-recovery-eligibility.js +0 -174
  244. package/dist/process-recovery-native.d.ts +0 -161
  245. package/dist/process-recovery-native.js +0 -595
  246. package/dist/process-recovery-provenance.d.ts +0 -163
  247. package/dist/process-recovery-provenance.js +0 -292
  248. package/dist/process-recovery-services.d.ts +0 -56
  249. package/dist/process-recovery-services.js +0 -191
  250. package/dist/process-recovery-settlement.d.ts +0 -32
  251. package/dist/process-recovery-settlement.js +0 -133
  252. package/dist/process-recovery.d.ts +0 -41
  253. package/dist/process-recovery.js +0 -91
  254. package/dist/session-brief.d.ts +0 -8
  255. package/dist/session-brief.js +0 -24
  256. package/dist/session-cli.d.ts +0 -42
  257. package/dist/session-cli.js +0 -271
  258. package/dist/session-contract.d.ts +0 -183
  259. package/dist/session-contract.js +0 -122
  260. package/dist/session-remote.d.ts +0 -19
  261. package/dist/session-remote.js +0 -86
  262. package/dist/session-service.d.ts +0 -22
  263. package/dist/session-service.js +0 -157
  264. package/dist/team-cli.js +0 -692
package/dist/provider.js CHANGED
@@ -999,32 +999,6 @@ export const MONEY_CAPABILITIES = withAcp({
999
999
  nativeDollarCapFlag: null,
1000
1000
  usageSemantics: "per-invocation",
1001
1001
  }));
1002
- /**
1003
- * The fail-closed budget-flag probe (finding 24's amendment): resolve
1004
- * the EXACT executable that will spawn, read its version, and prove the
1005
- * flag exists in that binary's own help — presence is a feature check
1006
- * and nothing more.
1007
- */
1008
- export async function probeBudgetCap(provider, runner) {
1009
- const flag = MONEY_CAPABILITIES[provider].nativeDollarCapFlag;
1010
- if (flag === null)
1011
- return { ok: false, problem: `${provider} has no native dollar cap` };
1012
- const binary = provider === "claude" ? "claude" : provider;
1013
- // A feature probe needs no credential (round 2, finding 2): strip them all.
1014
- const strip = { omitEnv: ALL_CREDENTIAL_ENV };
1015
- const where = await runner("which", [binary], { timeoutMs: 5_000, ...strip });
1016
- if (where.code !== 0)
1017
- return { ok: false, problem: `${binary} is not on PATH` };
1018
- const executable = where.stdout.trim().split("\n")[0] ?? "";
1019
- const version = await runner(executable, ["--version"], { timeoutMs: 10_000, ...strip });
1020
- if (version.code !== 0)
1021
- return { ok: false, problem: `${executable} did not answer --version` };
1022
- const help = await runner(executable, ["--help"], { timeoutMs: 10_000, ...strip });
1023
- if (help.code !== 0 || !help.stdout.includes(flag)) {
1024
- return { ok: false, problem: `${executable} does not advertise ${flag} — the dollar cap cannot be enforced` };
1025
- }
1026
- return { ok: true, executable, version: version.stdout.trim() };
1027
- }
1028
1002
  export function auditOf(provider) {
1029
1003
  return AUDITS[provider];
1030
1004
  }
package/dist/pulls.d.ts CHANGED
@@ -119,5 +119,3 @@ export declare function describe(pull: Pull, markers?: readonly string[]): strin
119
119
  * and not evidence of freshness.
120
120
  */
121
121
  export declare function idleDays(pull: Pull, now: Date): number | null;
122
- /** Waiting on someone, and has been for longer than they would have chosen. */
123
- export declare function isStalled(pull: Pull, now: Date, staleDays?: number, markers?: readonly string[]): boolean;
package/dist/pulls.js CHANGED
@@ -236,15 +236,6 @@ export function idleDays(pull, now) {
236
236
  return null;
237
237
  return Math.max(0, (now.getTime() - updated) / MS_PER_DAY);
238
238
  }
239
- /** Waiting on someone, and has been for longer than they would have chosen. */
240
- export function isStalled(pull, now, staleDays = DEFAULT_STALE_DAYS, markers = DEFAULT_REVISION_MARKERS) {
241
- const attention = classify(pull, markers);
242
- // Nobody is blocked on a draft, and a machine that is still working is not late.
243
- if (attention === "none" || attention === "machine")
244
- return false;
245
- const idle = idleDays(pull, now);
246
- return idle !== null && idle >= staleDays;
247
- }
248
239
  function firstLine(text) {
249
240
  const [line = ""] = text.trim().split("\n");
250
241
  return line;
@@ -54,7 +54,16 @@ export type AdapterOptions = {
54
54
  now?: () => Date;
55
55
  sleep?: (ms: number) => Promise<void>;
56
56
  };
57
+ /** How many times, and how far apart, a merged branch's deletion is looked for after the push lost a race. */
58
+ export declare const BRANCH_GONE_LOOKS = 5;
59
+ export declare const BRANCH_GONE_PAUSE_MS = 2000;
57
60
  export declare function releaseAdapters(options: AdapterOptions): ReleaseAdapters;
61
+ /**
62
+ * Whether a workflow's triggers (its top-level `on:` as a name, a list or a map) include a change to the repository.
63
+ * A file that doesn't parse, or whose triggers aren't one of those shapes, counts as running, so the release waits
64
+ * rather than skipping checks it owes.
65
+ */
66
+ export declare function workflowRunsOnChange(text: string): boolean;
58
67
  /** The formula with its first `url` and `sha256` replaced; anything else is left exactly as it was. */
59
68
  export declare function bumpFormula(text: string, url: string, sha256: string): string;
60
69
  export {};
@@ -10,6 +10,8 @@ import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
10
10
  import { homedir } from "node:os";
11
11
  import { basename, delimiter, dirname, join } from "node:path";
12
12
  import { createRequire } from "node:module";
13
+ import { parse as parseYaml } from "yaml";
14
+ import { sleep } from "./sleep.js";
13
15
  // Loaded on first use, like the store: a static import prints Node's SQLite warning before the CLI can quiet it.
14
16
  const sqlite = () => createRequire(import.meta.url)("node:sqlite");
15
17
  import { activeUpdateWork } from "./desktop-update-gate.js";
@@ -147,6 +149,9 @@ export function stageGate(stage, owner) {
147
149
  const id = textOr(journal["id"]), phase = textOr(journal["phase"]);
148
150
  return id !== null && phase !== null && BEFORE_SWAP.includes(phase) && owner() === id ? { id, phase } : null;
149
151
  }
152
+ /** How many times, and how far apart, a merged branch's deletion is looked for after the push lost a race. */
153
+ export const BRANCH_GONE_LOOKS = 5;
154
+ export const BRANCH_GONE_PAUSE_MS = 2_000;
150
155
  export function releaseAdapters(options) {
151
156
  const run = options.run ?? exec;
152
157
  const get = options.fetch ?? fetch;
@@ -154,6 +159,7 @@ export function releaseAdapters(options) {
154
159
  const tap = options.tap ?? "ap9000/homebrew-toolroll";
155
160
  const formula = options.formula ?? "Formula/toolroll.rb";
156
161
  const git = (...args) => run("git", ["-C", checkout, ...args]);
162
+ const pause = options.sleep ?? ((ms) => new Promise(done => setTimeout(done, ms)));
157
163
  const gh = (...args) => run("gh", args);
158
164
  const ghJson = async (what, ...args) => JSON.parse(await must(gh(...args), what));
159
165
  const toolroll = async (...args) => {
@@ -189,7 +195,7 @@ export function releaseAdapters(options) {
189
195
  });
190
196
  return {
191
197
  now: options.now ?? (() => new Date()),
192
- sleep: options.sleep ?? (ms => new Promise(done => setTimeout(done, ms))),
198
+ sleep: options.sleep ?? sleep,
193
199
  say: options.say ?? (line => process.stdout.write(`${line}\n`)),
194
200
  repo: {
195
201
  async github() {
@@ -225,7 +231,19 @@ export function releaseAdapters(options) {
225
231
  return;
226
232
  if (current !== head)
227
233
  throw new Error(`${branch} moved after merging; its new head was not deleted.`);
228
- await must(git("push", `--force-with-lease=${ref}:${head}`, "origin", `:${ref}`), `deleting merged branch ${branch}`);
234
+ const pushed = await git("push", `--force-with-lease=${ref}:${head}`, "origin", `:${ref}`);
235
+ if (pushed.code === 0)
236
+ return;
237
+ // GitHub may delete a merged branch itself between the look and the push; gone is what was wanted. Its refs can
238
+ // lag the deletion by a few seconds, so look a few times before calling it a failure.
239
+ for (let look = 0; look < BRANCH_GONE_LOOKS; look++) {
240
+ if (look > 0)
241
+ await pause(BRANCH_GONE_PAUSE_MS);
242
+ const after = (await must(git("ls-remote", "origin", ref), "git ls-remote")).trim().split(/\s+/)[0] || null;
243
+ if (after === null)
244
+ return;
245
+ }
246
+ throw new Error(`deleting merged branch ${branch} failed: ${pushed.stderr.trim() || `exit ${pushed.code}`}`);
229
247
  },
230
248
  async tree(sha) { return (await must(git("rev-parse", `${sha}^{tree}`), `git rev-parse ${sha.slice(0, 7)}^{tree}`)).trim(); },
231
249
  async fileAt(sha, path) {
@@ -338,10 +356,10 @@ export function releaseAdapters(options) {
338
356
  return prs.find(one => one.state === "open") ?? prs.filter(one => one.state === "merged").sort((a, b) => b.number - a.number)[0] ?? null;
339
357
  },
340
358
  async checks(repo, sha) {
341
- const out = await must(gh("api", "--paginate", `repos/${repo}/commits/${sha}/check-runs?per_page=100`, "--jq", ".check_runs[] | {id, name, status, conclusion}"), "gh api check-runs");
359
+ const out = await must(gh("api", "--paginate", `repos/${repo}/commits/${sha}/check-runs?per_page=100`, "--jq", ".check_runs[] | {id, name, status, conclusion, app: .app.slug}"), "gh api check-runs");
342
360
  const runs = out.split("\n").filter(Boolean).map(line => object(JSON.parse(line)));
343
361
  // Oldest first, so a rerun's attempt is the last one listed for its name.
344
- return runs.sort((a, b) => Number(a["id"]) - Number(b["id"])).map((one) => ({ name: String(one["name"]), status: String(one["status"]), conclusion: textOr(one["conclusion"]) }));
362
+ return runs.sort((a, b) => Number(a["id"]) - Number(b["id"])).map((one) => ({ name: String(one["name"]), status: String(one["status"]), conclusion: textOr(one["conclusion"]), app: textOr(one["app"]) }));
345
363
  },
346
364
  async merge(repo, pr, head) {
347
365
  const merged = object(await ghJson("gh api pulls merge", "api", "-X", "PUT", `repos/${repo}/pulls/${pr}/merge`, "-f", "merge_method=squash", "-f", `sha=${head}`));
@@ -379,6 +397,37 @@ export function releaseAdapters(options) {
379
397
  },
380
398
  homebrew: {
381
399
  repo: tap,
400
+ async checked(head) {
401
+ // Only a workflow that runs on pull requests (or pushes) puts checks on the tap's change; apps that open a
402
+ // check suite and never run it don't count. Workflows are read as the change has them and as the tap's default
403
+ // branch has them (pull_request_target runs from there); a file this can't read counts as checking.
404
+ for (const ref of [head, await defaultBranch()]) {
405
+ const listed = await gh("api", `repos/${tap}/contents/.github/workflows?ref=${encodeURIComponent(ref)}`, "--jq", ".[] | select(.type == \"file\") | .path");
406
+ if (listed.code !== 0) {
407
+ if (/Not Found|HTTP 404/.test(listed.stderr + listed.stdout))
408
+ continue;
409
+ throw new Error(`listing ${tap}'s workflows at ${ref}: ${listed.stderr.trim()}`);
410
+ }
411
+ for (const path of listed.stdout.split("\n").filter(Boolean)) {
412
+ if (!/\.ya?ml$/.test(path))
413
+ continue;
414
+ const file = await gh("api", `repos/${tap}/contents/${path}?ref=${encodeURIComponent(ref)}`, "--jq", ".content");
415
+ if (file.code !== 0)
416
+ return true;
417
+ if (workflowRunsOnChange(Buffer.from(file.stdout.replace(/\\n|\s/g, ""), "base64").toString("utf8")))
418
+ return true;
419
+ }
420
+ }
421
+ return false;
422
+ },
423
+ async statuses(head) {
424
+ // The older commit-status API, which some CI reports through instead of checks; newest first, so reversed.
425
+ const out = await must(gh("api", "--paginate", `repos/${tap}/commits/${head}/statuses?per_page=100`, "--jq", ".[] | {context, state}"), "gh api statuses");
426
+ return out.split("\n").filter(Boolean).map(line => object(JSON.parse(line))).reverse().map((one) => {
427
+ const state = String(one["state"]);
428
+ return { name: `status: ${String(one["context"])}`, status: state === "pending" ? "in_progress" : "completed", conclusion: state === "pending" ? null : state === "success" ? "success" : "failure" };
429
+ });
430
+ },
382
431
  async state(version) {
383
432
  const branch = await defaultBranch();
384
433
  const current = await formulaAt(branch);
@@ -427,6 +476,34 @@ export function releaseAdapters(options) {
427
476
  }
428
477
  }
429
478
  const brewBranch = (version) => `toolroll-${version}`;
479
+ /** The events that put checks on a change. */
480
+ const CHANGE_EVENTS = new Set(["pull_request", "pull_request_target", "push", "merge_group"]);
481
+ /**
482
+ * Whether a workflow's triggers (its top-level `on:` as a name, a list or a map) include a change to the repository.
483
+ * A file that doesn't parse, or whose triggers aren't one of those shapes, counts as running, so the release waits
484
+ * rather than skipping checks it owes.
485
+ */
486
+ export function workflowRunsOnChange(text) {
487
+ let parsed;
488
+ try {
489
+ parsed = parseYaml(text);
490
+ }
491
+ catch {
492
+ return true;
493
+ }
494
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
495
+ return true;
496
+ const workflow = parsed;
497
+ // YAML 1.1 readers take a bare `on` for true; either key names the triggers.
498
+ const on = "on" in workflow ? workflow["on"] : workflow["true"];
499
+ const events = typeof on === "string" ? [on]
500
+ : Array.isArray(on) ? on
501
+ : on !== null && typeof on === "object" ? Object.keys(on)
502
+ : null;
503
+ if (events === null || !events.every(one => typeof one === "string"))
504
+ return true;
505
+ return events.some(one => CHANGE_EVENTS.has(one));
506
+ }
430
507
  /** The formula with its first `url` and `sha256` replaced; anything else is left exactly as it was. */
431
508
  export function bumpFormula(text, url, sha256) {
432
509
  if (!/^\s*url "[^"]*"/m.test(text) || !/^\s*sha256 "[0-9a-f]{64}"/m.test(text))
package/dist/release.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * skipped or failed never merges. merge
18
18
  * squash-merges with the head pinned to the gated commit, tree proves main's new commit has exactly the gated tree,
19
19
  * and only then tag pushes the version tag, whose macOS matrix must pass before npm publication (publish.yml). publish
20
- * waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it.
20
+ * waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it (after its checks, when the tap runs any).
21
21
  *
22
22
  * A private journal (mode 0600, written whole then renamed) records what each step is about to do before it does
23
23
  * anything outside this machine, and what it saw after. A stop — a time limit, a failure, a crash, Ctrl-C — leaves the
@@ -38,10 +38,12 @@ export type ReleaseStep = typeof RELEASE_STEPS[number];
38
38
  type WaitingStep = "approval" | "check" | "quiet" | "deploy" | "ci" | "publish" | "homebrew";
39
39
  /** How long each waiting step may take, in minutes, before the release stops there (rerunning continues from it). */
40
40
  export declare const RELEASE_LIMITS: Readonly<Record<WaitingStep, number>>;
41
+ /** One check attempt; `app` is the GitHub App that made it (github-actions for a workflow), when known. */
41
42
  export type CheckRun = {
42
43
  name: string;
43
44
  status: string;
44
45
  conclusion: string | null;
46
+ app?: string | null;
45
47
  };
46
48
  export type PullRequest = {
47
49
  number: number;
@@ -195,6 +197,10 @@ export type ReleaseAdapters = {
195
197
  };
196
198
  homebrew: {
197
199
  repo: string;
200
+ /** Whether any of the tap's workflows, at the change or its base, runs on a change; if none does, no checks are owed. */
201
+ checked(head: string): Promise<boolean>;
202
+ /** The change's commit statuses, as check runs oldest first. */
203
+ statuses(head: string): Promise<CheckRun[]>;
198
204
  state(version: string): Promise<HomebrewState>;
199
205
  propose(version: string): Promise<{
200
206
  pr: number;
package/dist/release.js CHANGED
@@ -17,7 +17,7 @@
17
17
  * skipped or failed never merges. merge
18
18
  * squash-merges with the head pinned to the gated commit, tree proves main's new commit has exactly the gated tree,
19
19
  * and only then tag pushes the version tag, whose macOS matrix must pass before npm publication (publish.yml). publish
20
- * waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it.
20
+ * waits for npm, the GitHub release and the macOS checks; homebrew updates the tap's formula and merges it (after its checks, when the tap runs any).
21
21
  *
22
22
  * A private journal (mode 0600, written whole then renamed) records what each step is about to do before it does
23
23
  * anything outside this machine, and what it saw after. A stop — a time limit, a failure, a crash, Ctrl-C — leaves the
@@ -454,15 +454,28 @@ export async function runRelease(options, adapters) {
454
454
  const proposed = state;
455
455
  j.homebrew = { pr: proposed.pr, head: proposed.head, merged: false };
456
456
  save();
457
+ // Whatever checks do register are always judged; an empty set passes only for a tap whose workflows never run
458
+ // on a change, and only when it's still empty a poll later.
459
+ const checked = await brew.checked(proposed.head);
460
+ let emptyLooks = 0;
457
461
  await waitFor("homebrew", `the Homebrew change's checks (${brew.repo} #${proposed.pr})`, async () => {
458
- const runs = await adapters.github.checks(brew.repo, proposed.head);
462
+ // Check runs and commit statuses are judged apart, so neither stands in for the other or hides its failure.
463
+ // Two apps may name a check alike; each app's attempts stand for themselves.
464
+ const runs = (await adapters.github.checks(brew.repo, proposed.head)).map(run => ({ ...run, name: `${run.app ?? "unknown app"}: ${run.name}` }));
465
+ const statuses = await brew.statuses(proposed.head);
466
+ const verdicts = [runs, statuses].filter(one => one.length > 0).map(one => checksVerdict(one, [...new Set(one.map(run => run.name))]));
467
+ const failed = verdicts.flatMap(one => one.state === "failed" ? one.failed : []);
468
+ if (failed.length > 0)
469
+ throw new Stop("homebrew-failed", `The Homebrew change's checks failed: ${failed.join(", ")}.`);
470
+ if (verdicts.some(one => one.state === "pending"))
471
+ return null;
459
472
  // GitHub registers checks asynchronously; an empty set says nothing about success.
460
- if (runs.length === 0)
473
+ // A workflow is owed: only its own run counts, not another app's green check.
474
+ if (checked && !runs.some(run => run.app === "github-actions"))
461
475
  return null;
462
- const verdict = checksVerdict(runs, [...new Set(runs.map(one => one.name))]);
463
- if (verdict.state === "failed")
464
- throw new Stop("homebrew-failed", `The Homebrew change's checks failed: ${verdict.failed.join(", ")}.`);
465
- return verdict.state === "passed" ? true : null;
476
+ if (runs.length === 0 && statuses.length === 0)
477
+ return ++emptyLooks >= 2 ? true : null;
478
+ return true;
466
479
  });
467
480
  await brew.merge(proposed.pr, proposed.head);
468
481
  }
@@ -1,8 +1,9 @@
1
- import { type TeamCliOptions } from './team-cli.js';
1
+ import { type ConnectOptions } from './connect-cli.js';
2
2
  import { type CliRequest } from './cli-http.js';
3
3
  import { type RemoteCommandLookup } from './remote-command.js';
4
4
  export { contractRow, STEP_UP_MESSAGE, type RemoteMode } from './remote-command.js';
5
- export type RemoteExecOptions = TeamCliOptions & {
5
+ export type RemoteExecOptions = ConnectOptions & {
6
+ sleep?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
6
7
  cwd?: string;
7
8
  /** Raw output streams; remote bytes are written exactly as the server returned them. */
8
9
  stdout?: (chunk: string) => void;
@@ -12,13 +12,14 @@ import { resolve } from 'node:path';
12
12
  import { setTimeout as delay } from 'node:timers/promises';
13
13
  import { envelopeJson } from './envelope.js';
14
14
  import { COMMAND_GUIDE } from './surface.js';
15
- import { centralProfile, UsageError } from './team-cli.js';
15
+ import { centralProfile, UsageError } from './connect-cli.js';
16
16
  import { CLI_FILES_BYTES, CLI_WAIT_SECONDS } from './cli-http.js';
17
17
  import { TOKEN_REPLACEMENT } from './password-bearer.js';
18
18
  import { contractRow, fileArguments, STEP_UP_MESSAGE } from './remote-command.js';
19
19
  export { contractRow, STEP_UP_MESSAGE } from './remote-command.js';
20
20
  const EXIT = { ok: 0, failed: 1, usage: 2, refused: 3 };
21
- /** Team commands own their own central path (team-cli.ts); `session` sends its own checked request (session-cli.ts). */
21
+ /** Never sent remotely: `connect` saves the profile itself, the lead's commands are this computer's, and the removed
22
+ * central team and session verbs answer with their removal notice (removed-commands.ts). */
22
23
  const TEAM_COMMANDS = new Set(['connect', 'lead', 'conversation', 'chat', 'brief', 'session']);
23
24
  const RESPONSE_BYTES = 16 * 1024 * 1024;
24
25
  const REQUEST_TIMEOUT_MS = (CLI_WAIT_SECONDS + 35) * 1_000;
package/dist/repos.d.ts CHANGED
@@ -50,11 +50,6 @@ export declare function updateRepos(file: string, transform: (repos: string[]) =
50
50
  ok: true;
51
51
  repos: string[];
52
52
  } | RegistryUpdateFailure>;
53
- /** Persist project roots through the same lock as repo enrollment. */
54
- export declare function updateProjectRoots(file: string, transform: (roots: string[]) => string[]): Promise<{
55
- ok: true;
56
- roots: string[];
57
- } | RegistryUpdateFailure>;
58
53
  /** Atomically update repositories and roots without either writer erasing the other. */
59
54
  export declare function updateProjectRegistry(file: string, transform: (registry: ProjectRegistry) => ProjectRegistry): Promise<{
60
55
  ok: true;
package/dist/repos.js CHANGED
@@ -11,8 +11,9 @@
11
11
  * untrusted input and gets validated like any other.
12
12
  */
13
13
  import { mkdir, readFile, writeFile } from "node:fs/promises";
14
- import { dirname, join } from "node:path";
14
+ import { dirname } from "node:path";
15
15
  import { configBase, namedPath } from "./names.js";
16
+ import { sleep } from "./sleep.js";
16
17
  export const CONFIG_VERSION = 2;
17
18
  const FILE_NAME = "repos.json";
18
19
  export function configPath(env, home) {
@@ -76,14 +77,6 @@ export async function updateRepos(file, transform) {
76
77
  }));
77
78
  return updated.ok ? { ok: true, repos: updated.registry.repos } : updated;
78
79
  }
79
- /** Persist project roots through the same lock as repo enrollment. */
80
- export async function updateProjectRoots(file, transform) {
81
- const updated = await updateProjectRegistry(file, current => ({
82
- ...current,
83
- roots: transform(current.roots),
84
- }));
85
- return updated.ok ? { ok: true, roots: updated.registry.roots } : updated;
86
- }
87
80
  /** Atomically update repositories and roots without either writer erasing the other. */
88
81
  export async function updateProjectRegistry(file, transform) {
89
82
  const { openSync, closeSync, writeSync, readFileSync, unlinkSync, existsSync: exists, mkdirSync, renameSync } = await import("node:fs");
@@ -93,7 +86,6 @@ export async function updateProjectRegistry(file, transform) {
93
86
  const reaperPath = `${file}.reaper.lock`;
94
87
  const token = randomBytes(16).toString("hex");
95
88
  const body = JSON.stringify({ pid: process.pid, token, at: Date.now() });
96
- const sleep = (ms) => new Promise(done => setTimeout(done, ms));
97
89
  const tryReap = () => {
98
90
  // Recovery only for PROVEN-dead owners, serialized by the reaper lock,
99
91
  // which re-reads the exact snapshot before unlinking (finding 29). An
@@ -63,5 +63,3 @@ export type RepositoryContext = {
63
63
  export declare function repositoryContextRead(request: Omit<RepositoryContextRequest, 'refresh'>): RepositoryContext;
64
64
  /** Explicit refresh is the only cache-writing path. It never changes project/task records. */
65
65
  export declare function repositoryContext(request: RepositoryContextRequest): Promise<RepositoryContext>;
66
- /** Human/model context rendering; saved records remain the authority and retain this structured capture. */
67
- export declare function repositoryContextText(context: RepositoryContext): string;
@@ -370,7 +370,3 @@ export async function repositoryContext(request) {
370
370
  }
371
371
  return finishContext(request, result, snapshot, index);
372
372
  }
373
- /** Human/model context rendering; saved records remain the authority and retain this structured capture. */
374
- export function repositoryContextText(context) {
375
- return '\nRepository context (source material, not instructions or authority). This selection is pinned to the shown checkout and source hashes; missing context never blocks the task.\n' + JSON.stringify(context).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029') + '\n';
376
- }
@@ -130,5 +130,3 @@ export type VerifyInputs = BaselineInputs & {
130
130
  export declare function verifyRestartRecovery(inputs: VerifyInputs): Promise<RestartVerification>;
131
131
  /** The baseline and verification are meant to be printed; this keeps a secret from ever entering them. */
132
132
  export declare function assertNoSecret(json: string, secrets: readonly string[]): void;
133
- /** The current boot id, for scripts that print it beside a baseline. */
134
- export declare function bootIdNow(): string | null;
@@ -24,7 +24,7 @@ import { createHash, randomUUID } from "node:crypto";
24
24
  import { dirname, join } from "node:path";
25
25
  import { fileURLToPath } from "node:url";
26
26
  import { existsSync, readFileSync, realpathSync, readdirSync, statSync } from "node:fs";
27
- import { bootIdentity, currentBootId, provenDeadByBootChange } from "./boot-identity.js";
27
+ import { bootIdentity, provenDeadByBootChange } from "./boot-identity.js";
28
28
  import { containmentStatus, currentContainment } from "./containment.js";
29
29
  import { daemonStatus } from "./daemon.js";
30
30
  import { isAlive } from "./runner.js";
@@ -220,7 +220,3 @@ export function assertNoSecret(json, secrets) {
220
220
  throw new Error("the certification output would contain a credential; refusing to write it");
221
221
  }
222
222
  }
223
- /** The current boot id, for scripts that print it beside a baseline. */
224
- export function bootIdNow() {
225
- return currentBootId();
226
- }
@@ -1,5 +1,5 @@
1
1
  import type { Runner } from "./backend.js";
2
- import type { Store, VerifyCommand } from "./store.js";
2
+ import type { Store } from "./store.js";
3
3
  import { type CheckLevel } from "./check-levels.js";
4
4
  export declare const CHECKS_REQUESTED = "checks requested";
5
5
  export declare const CHECKS_STARTED = "checks started";
@@ -93,8 +93,6 @@ export declare function withFollowUps(original: CheckReading, followUps: readonl
93
93
  /** The level a result's checks stand at now: the build's own level, raised by
94
94
  * a follow-up that passed on the same commit. null for results from before levels. */
95
95
  export declare function resultCheckLevel(store: Store, runId: number, now: Date): CheckLevel | null;
96
- /** Whether this result's commit has passed the project's full check, here or in a follow-up. */
97
- export declare function fullCheckPassed(reading: CheckReading): boolean;
98
96
  /** Before a Quick or Off result merges: the project's full check on its exact
99
97
  * commit. Clear when the build already ran the full check, when the project has
100
98
  * no full command (its CI covers it), or when a follow-up full check passed. */
@@ -105,7 +103,6 @@ export type FullCheckGate = {
105
103
  message: string;
106
104
  };
107
105
  export declare function fullCheckGate(store: Store, runId: number, now: Date): FullCheckGate;
108
- export declare const isQuickCommand: (command: VerifyCommand | null, repo: string) => boolean;
109
106
  /** File a small task to write tests for a result's change. It is filed like any
110
107
  * other task: nothing runs until a person approves it. */
111
108
  export declare function fileAddTestsTask(store: Store, root: string | null, input: {
@@ -9,6 +9,7 @@
9
9
  *
10
10
  * Add tests files a small task, unapproved like any other filing, to write
11
11
  * tests for that change. */
12
+ import { fencedCheckoutCommands } from "./invoke.js";
12
13
  import { mkdtempSync, rmSync } from "node:fs";
13
14
  import { tmpdir } from "node:os";
14
15
  import { join } from "node:path";
@@ -16,7 +17,7 @@ import { run as execRun } from "./exec.js";
16
17
  import { approvedCommandShell, SETUP_ENV_ALLOWLIST, SETUP_ENV_DENYLIST } from "./builder.js";
17
18
  import { runWithIsolatedDatabase } from "./child-database.js";
18
19
  import { boundStreamHeadTail, readVerifiedArtifact, redactSecretLines, scanForSecrets, storeEvidence } from "./evidence.js";
19
- import { CHECK_LEVEL_WORDS, checkCommandFor, quickVerifyKey, repoClause, repoOfRun, runCheckLevel } from "./check-levels.js";
20
+ import { CHECK_LEVEL_WORDS, checkCommandFor, repoClause, repoOfRun, runCheckLevel } from "./check-levels.js";
20
21
  import { fileTaskProposal } from "./proposal.js";
21
22
  export const CHECKS_REQUESTED = "checks requested";
22
23
  export const CHECKS_STARTED = "checks started";
@@ -201,7 +202,7 @@ export async function runFollowUpCheck(store, root, request, deps = {}) {
201
202
  let ready = true;
202
203
  if (setup !== null) {
203
204
  const shell = approvedCommandShell(setup.command);
204
- const prepared = await runWithIsolatedDatabase(runner, shell.file, shell.args, { cwd: checkout, timeoutMs: setup.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
205
+ const prepared = await runWithIsolatedDatabase(fencedCheckoutCommands(store, target.repo, checkout, runner), shell.file, shell.args, { cwd: checkout, timeoutMs: setup.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
205
206
  note("Approved project setup", setup.command, prepared);
206
207
  if (prepared.code !== 0 || prepared.timedOut || prepared.notFound) {
207
208
  ready = false;
@@ -216,7 +217,7 @@ export async function runFollowUpCheck(store, root, request, deps = {}) {
216
217
  }
217
218
  else {
218
219
  const shell = approvedCommandShell(live.command);
219
- const ran = await runWithIsolatedDatabase(runner, shell.file, shell.args, { cwd: checkout, timeoutMs: live.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
220
+ const ran = await runWithIsolatedDatabase(fencedCheckoutCommands(store, target.repo, checkout, runner), shell.file, shell.args, { cwd: checkout, timeoutMs: live.timeoutMs, processGroup: true, envAllowlist: SETUP_ENV_ALLOWLIST, omitEnv: SETUP_ENV_DENYLIST });
220
221
  note(`${CHECK_LEVEL_WORDS[target.level]} check`, live.command, ran);
221
222
  result = ran.timedOut ? { ran: false, failure: "The check ran out of time." } : ran.notFound ? { ran: false, failure: "The check couldn't start." } : { ran: true, exitCode: ran.code };
222
223
  }
@@ -283,10 +284,6 @@ export function resultCheckLevel(store, runId, now) {
283
284
  const read = withFollowUps({ status: level === "off" ? "not-run" : "passed", level, exitCode: null, head: run?.headRevision ?? null }, follows);
284
285
  return read.level;
285
286
  }
286
- /** Whether this result's commit has passed the project's full check, here or in a follow-up. */
287
- export function fullCheckPassed(reading) {
288
- return reading.status === "passed" && (reading.level === "full" || reading.level === null);
289
- }
290
287
  export function fullCheckGate(store, runId, now) {
291
288
  const level = runCheckLevel(store, runId);
292
289
  const repo = repoOfRun(store, runId);
@@ -309,7 +306,6 @@ export function fullCheckGate(store, runId, now) {
309
306
  return { state: "not-run", message: `The full check didn't finish on this commit${last.note === null ? "" : `: ${last.note}`} Run it again, or merge anyway.` };
310
307
  return { state: "waiting", message: "The full check is still running on this commit. Merge once it passes, or merge anyway." };
311
308
  }
312
- export const isQuickCommand = (command, repo) => command !== null && command.repo === quickVerifyKey(repo);
313
309
  // ---- Add tests --------------------------------------------------------------
314
310
  /** File a small task to write tests for a result's change. It is filed like any
315
311
  * other task: nothing runs until a person approves it. */
@@ -182,8 +182,6 @@ export declare function parseRevisionBatch(raw: string | null | undefined): numb
182
182
  /** The source binding the revision form carries: the scope digest the
183
183
  * page was rendered against, or "none". */
184
184
  export declare function revisionSourceOf(scopeDigest: string | null): string;
185
- export declare const REVIEW_DRAFT_PREFIX = "standing-orders:review-draft:";
186
- export declare const RESULT_SCROLL_PREFIX = "standing-orders:result-scroll:";
187
185
  /** The in-page half of the result presentation. Everything it does is
188
186
  * presentational or a bounded draft: no fetch, no endpoint, no submit.
189
187
  *
@@ -210,8 +210,6 @@ export function parseRevisionBatch(raw) {
210
210
  export function revisionSourceOf(scopeDigest) {
211
211
  return scopeDigest === null ? "none" : scopeDigest;
212
212
  }
213
- export const REVIEW_DRAFT_PREFIX = "standing-orders:review-draft:";
214
- export const RESULT_SCROLL_PREFIX = "standing-orders:result-scroll:";
215
213
  /** The in-page half of the result presentation. Everything it does is
216
214
  * presentational or a bounded draft: no fetch, no endpoint, no submit.
217
215
  *
@@ -12,7 +12,6 @@ export declare const RETENTION_KINDS: readonly {
12
12
  }[];
13
13
  export declare const PERIOD_CHOICES: Readonly<Record<RetentionKind, readonly (number | null)[]>>;
14
14
  export declare const MIN_DAYS: 1;
15
- export declare const MAX_DAYS: 3650;
16
15
  /** A sweep is due once a day. */
17
16
  export declare const SWEEP_EVERY_MS = 86400000;
18
17
  /** At most this many runs, messages, notifications or records go per kind per sweep; the next sweep takes the rest. */
package/dist/retention.js CHANGED
@@ -69,7 +69,6 @@ export const PERIOD_CHOICES = {
69
69
  evidence: [1, 7, 14, 28, null], checkouts: LONG_CHOICES, chat: LONG_CHOICES, notifications: LONG_CHOICES,
70
70
  };
71
71
  export const MIN_DAYS = RETENTION_DAYS.min;
72
- export const MAX_DAYS = RETENTION_DAYS.max;
73
72
  const DAY_MS = 86_400_000;
74
73
  /** A sweep is due once a day. */
75
74
  export const SWEEP_EVERY_MS = DAY_MS;
@@ -32,7 +32,6 @@ export declare const REVIEW_CONTEXT_LIMITS: {
32
32
  /** Only this compact projection enters prompts. Full bytes remain sealed in
33
33
  * the artifact and are materialized under generated, flat scratch names. */
34
34
  export declare function reviewContextFileName(id: string): string;
35
- export declare function reviewContextManifest(inventory: ReviewContextInventory): string;
36
35
  export type ContextGapReason = "source-missing" | "source-unverified" | "source-proof-missing" | "source-scope-changed" | "criterion-changed" | "stale-ancestry" | "missing-at-head" | "not-a-file" | "binary" | "over-limit" | "aggregate-limit" | "item-cap" | "secret-redacted" | "capture-failed";
37
36
  export type ContextItemWhy = "source-criterion-evidence" | "source-changed-path" | "candidate-changed-path";
38
37
  export type ReviewContextItem = {
@@ -1,5 +1,5 @@
1
1
  // Assignment direct assessments reuse custody checks; gaps remain explicit.
2
- import { readObservationEvidence, OBSERVATION_CAPTURE, OBSERVATION_FILE } from "./observations.js";
2
+ import { readObservationEvidence, OBSERVATION_CAPTURE } from "./observations.js";
3
3
  import { isVerificationReceipt } from "./verification-evidence.js";
4
4
  /**
5
5
  * Inherited review context (v51, contract handoff task 3): the bounded,
@@ -75,17 +75,6 @@ export function reviewContextFileName(id) {
75
75
  throw new Error("invalid context item id");
76
76
  return `REVIEW-CONTEXT-${id}.txt`;
77
77
  }
78
- export function reviewContextManifest(inventory) {
79
- return JSON.stringify({
80
- ...inventory,
81
- ...(inventory.handoff === undefined ? {} : { handoff: { artifact: inventory.handoff.artifact, sha256: inventory.handoff.sha256, file: "REVIEW-BUILDER-NOTES.json" } }),
82
- ...(inventory.observations === undefined ? {} : { observations: { artifact: inventory.observations.artifact, sha256: inventory.observations.sha256, file: OBSERVATION_FILE } }),
83
- format: "review-context-manifest-v1",
84
- items: inventory.items.map(({ content: _content, ...item }) => ({
85
- ...item, file: reviewContextFileName(item.id),
86
- })),
87
- });
88
- }
89
78
  const CONTEXT_INPUT_KINDS = new Set(["terminal-diff", "diff-stat", "proof", "check-log", "screenshot", "review-context"]);
90
79
  /** A shortened check log is the exact bounded log the reviewer saw.
91
80
  * Completeness of patches, proofs and other structured inputs is still
@@ -8,22 +8,6 @@ import { type BuildFinding } from "./contracts/review-findings.js";
8
8
  * screenshots only when this run actually has one (v40) — a grandfathered
9
9
  * run (no rubric) writes exactly what it always has, byte for byte. */
10
10
  export declare const REVIEW_PATCH_NAME = "REVIEW-DIFF.patch";
11
- export declare const REVIEW_RUBRIC_NAME = "REVIEW-RUBRIC.json";
12
- export declare const REVIEW_PROOF_NAME = "REVIEW-PROOF.json";
13
- /** Audit hardening (still evidence-review-v1, unreleased): the repository's
14
- * approved verification command's own captured stdout/stderr — a
15
- * criterion citing `check` evidence was previously judged from the proof's
16
- * bare claim, never the actual output. */
17
- export declare const REVIEW_CHECK_LOG_NAME = "REVIEW-CHECK-LOG.txt";
18
- /** v51 (inherited review context): a REVISION's sealed source-and-ancestry
19
- * inventory — bounded files at the exact sealed head, bound to the source
20
- * run, plus prior review provenance as context. Materialized only when
21
- * the run captured one; every other run writes exactly what it always has. */
22
- export declare const REVIEW_CONTEXT_NAME = "REVIEW-CONTEXT.json";
23
- /** One claimed screenshot's name in the scratch — stable per artifact id,
24
- * so a reviewer's own tool output naming it is reproducible across a
25
- * retried materialization within the same pass. */
26
- export declare function reviewScreenshotName(artifactId: number, kind: "png" | "jpeg"): string;
27
11
  export declare const REVIEW_LIMITS: {
28
12
  readonly comments: 40;
29
13
  readonly criteria: 12;
package/dist/reviewer.js CHANGED
@@ -6,24 +6,6 @@ import { readBuildFindings } from "./contracts/review-findings.js";
6
6
  * screenshots only when this run actually has one (v40) — a grandfathered
7
7
  * run (no rubric) writes exactly what it always has, byte for byte. */
8
8
  export const REVIEW_PATCH_NAME = "REVIEW-DIFF.patch";
9
- export const REVIEW_RUBRIC_NAME = "REVIEW-RUBRIC.json";
10
- export const REVIEW_PROOF_NAME = "REVIEW-PROOF.json";
11
- /** Audit hardening (still evidence-review-v1, unreleased): the repository's
12
- * approved verification command's own captured stdout/stderr — a
13
- * criterion citing `check` evidence was previously judged from the proof's
14
- * bare claim, never the actual output. */
15
- export const REVIEW_CHECK_LOG_NAME = "REVIEW-CHECK-LOG.txt";
16
- /** v51 (inherited review context): a REVISION's sealed source-and-ancestry
17
- * inventory — bounded files at the exact sealed head, bound to the source
18
- * run, plus prior review provenance as context. Materialized only when
19
- * the run captured one; every other run writes exactly what it always has. */
20
- export const REVIEW_CONTEXT_NAME = "REVIEW-CONTEXT.json";
21
- /** One claimed screenshot's name in the scratch — stable per artifact id,
22
- * so a reviewer's own tool output naming it is reproducible across a
23
- * retried materialization within the same pass. */
24
- export function reviewScreenshotName(artifactId, kind) {
25
- return `REVIEW-SCREENSHOT-${artifactId}.${kind === "png" ? "png" : "jpg"}`;
26
- }
27
9
  export const REVIEW_LIMITS = {
28
10
  ...REVIEW_OUTPUT_LIMITS,
29
11
  /** The mailbox read cap: 40 maximal comments plus 12 judgements fit with headroom. */