toolroll 0.9.92 → 0.9.94

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 (341) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/dist/activity-line.d.ts +7 -4
  3. package/dist/activity-line.js +9 -7
  4. package/dist/agent-fence.d.ts +4 -0
  5. package/dist/agent-fence.js +4 -1
  6. package/dist/approval-authority.d.ts +7 -1
  7. package/dist/approval-authority.js +11 -2
  8. package/dist/approval-password.d.ts +31 -0
  9. package/dist/approval-password.js +26 -0
  10. package/dist/assignment-status.d.ts +1 -1
  11. package/dist/assignment-ui.js +3 -3
  12. package/dist/assignment.js +16 -3
  13. package/dist/boot-identity.d.ts +2 -1
  14. package/dist/boot-identity.js +6 -4
  15. package/dist/bounded-spawn.d.ts +57 -0
  16. package/dist/bounded-spawn.js +220 -0
  17. package/dist/browser/terminal.js +42 -42
  18. package/dist/browser/workspace.css +2 -2
  19. package/dist/browser/workspace.js +26 -26
  20. package/dist/browser-crew.js +2 -2
  21. package/dist/browser-shell.d.ts +1 -1
  22. package/dist/browser-shell.js +2 -2
  23. package/dist/browser-workspace.d.ts +48 -7
  24. package/dist/browser-workspace.js +3 -3
  25. package/dist/builder-restart.d.ts +21 -0
  26. package/dist/builder-restart.js +50 -0
  27. package/dist/builder.d.ts +4 -0
  28. package/dist/builder.js +73 -25
  29. package/dist/chat-acceptance.js +1 -1
  30. package/dist/chat-actions.d.ts +12 -2
  31. package/dist/chat-actions.js +25 -2
  32. package/dist/chat-channel.js +11 -0
  33. package/dist/chat-decide.d.ts +7 -2
  34. package/dist/chat-decide.js +37 -9
  35. package/dist/chat-quiet.js +3 -3
  36. package/dist/chat-voice.d.ts +1 -1
  37. package/dist/chat-voice.js +2 -2
  38. package/dist/check-facts.d.ts +126 -0
  39. package/dist/check-facts.js +318 -0
  40. package/dist/check-failure.d.ts +22 -3
  41. package/dist/check-failure.js +57 -13
  42. package/dist/check-levels-ui.d.ts +4 -0
  43. package/dist/check-levels-ui.js +10 -1
  44. package/dist/check-load.d.ts +85 -0
  45. package/dist/check-load.js +147 -0
  46. package/dist/check-only-standing.d.ts +36 -0
  47. package/dist/check-only-standing.js +39 -0
  48. package/dist/check-only.d.ts +15 -0
  49. package/dist/check-only.js +99 -0
  50. package/dist/check-report.d.ts +7 -0
  51. package/dist/check-report.js +4 -3
  52. package/dist/check-stamp.d.ts +46 -0
  53. package/dist/check-stamp.js +93 -0
  54. package/dist/checkout-cleanup.d.ts +36 -5
  55. package/dist/checkout-cleanup.js +166 -14
  56. package/dist/cli-help.d.ts +4 -1
  57. package/dist/cli-help.js +47 -30
  58. package/dist/cli.js +16 -1
  59. package/dist/code-extract.d.ts +44 -0
  60. package/dist/code-extract.js +88 -0
  61. package/dist/code-map-acts.d.ts +24 -0
  62. package/dist/code-map-acts.js +53 -0
  63. package/dist/code-map-admission.d.ts +52 -0
  64. package/dist/code-map-admission.js +295 -0
  65. package/dist/code-map-build.d.ts +63 -0
  66. package/dist/code-map-build.js +415 -0
  67. package/dist/code-map-cli.d.ts +153 -0
  68. package/dist/code-map-cli.js +103 -0
  69. package/dist/code-map-main.d.ts +1 -0
  70. package/dist/code-map-main.js +48 -0
  71. package/dist/code-map-read.d.ts +106 -0
  72. package/dist/code-map-read.js +171 -0
  73. package/dist/code-map-report.d.ts +37 -0
  74. package/dist/code-map-report.js +95 -0
  75. package/dist/code-map-shape.d.ts +172 -0
  76. package/dist/code-map-shape.js +327 -0
  77. package/dist/code-map-store.d.ts +136 -0
  78. package/dist/code-map-store.js +233 -0
  79. package/dist/coding-provider.d.ts +2 -0
  80. package/dist/coding-provider.js +3 -2
  81. package/dist/coding-update.js +3 -2
  82. package/dist/coding-workspace.js +4 -4
  83. package/dist/console-chrome.js +2 -2
  84. package/dist/containment.d.ts +5 -0
  85. package/dist/containment.js +14 -1
  86. package/dist/contracts/chat-actions.d.ts +9 -0
  87. package/dist/contracts/chat-actions.js +2 -0
  88. package/dist/contracts/cli.d.ts +52 -0
  89. package/dist/contracts/cli.js +4 -0
  90. package/dist/contracts/console-api.d.ts +12 -5
  91. package/dist/contracts/console-api.js +12 -5
  92. package/dist/contracts/gateway-tools.d.ts +17 -0
  93. package/dist/contracts/gateway-tools.js +3 -1
  94. package/dist/contracts/lead-tools.d.ts +61 -0
  95. package/dist/contracts/lead-tools.js +10 -1
  96. package/dist/contracts/project-knowledge.d.ts +38 -5
  97. package/dist/contracts/project-knowledge.js +11 -5
  98. package/dist/contracts/project-memory.d.ts +3 -1
  99. package/dist/contracts/project-memory.js +3 -2
  100. package/dist/contracts/release-check.d.ts +25 -1
  101. package/dist/contracts/release-check.js +19 -0
  102. package/dist/contracts/storage.d.ts +14 -0
  103. package/dist/contracts/storage.js +3 -1
  104. package/dist/contracts/store-json.d.ts +2 -0
  105. package/dist/contracts/youiyoux-request.d.ts +1 -1
  106. package/dist/contracts/youiyoux-request.js +1 -1
  107. package/dist/desktop-access.js +6 -2
  108. package/dist/desktop-update.d.ts +2 -2
  109. package/dist/desktop-update.js +13 -9
  110. package/dist/dispatch.js +18 -15
  111. package/dist/dispose.js +8 -2
  112. package/dist/easy-start.js +2 -3
  113. package/dist/eval-hooks.d.ts +55 -0
  114. package/dist/eval-hooks.js +227 -4
  115. package/dist/evals.js +2 -1
  116. package/dist/fast-start.d.ts +136 -0
  117. package/dist/fast-start.js +431 -0
  118. package/dist/fast-terms.d.ts +9 -0
  119. package/dist/fast-terms.js +23 -0
  120. package/dist/fenced-check-coverage.d.ts +62 -0
  121. package/dist/fenced-check-coverage.js +54 -0
  122. package/dist/first-run.js +1 -1
  123. package/dist/flaky-ci.d.ts +31 -0
  124. package/dist/flaky-ci.js +149 -0
  125. package/dist/flaky-cli.d.ts +60 -0
  126. package/dist/flaky-cli.js +60 -0
  127. package/dist/flaky.d.ts +83 -0
  128. package/dist/flaky.js +264 -0
  129. package/dist/flow-card-task.js +3 -0
  130. package/dist/flow-triggers.d.ts +1 -1
  131. package/dist/flow-triggers.js +1 -1
  132. package/dist/github-import.d.ts +62 -0
  133. package/dist/github-import.js +146 -0
  134. package/dist/guides.js +15 -10
  135. package/dist/invoke.d.ts +5 -1
  136. package/dist/invoke.js +49 -14
  137. package/dist/job-object-helper.ps1 +10 -3
  138. package/dist/keymap.d.ts +41 -0
  139. package/dist/keymap.js +51 -0
  140. package/dist/knowledge-cli.js +1 -1
  141. package/dist/knowledge-ui.d.ts +21 -4
  142. package/dist/knowledge-ui.js +75 -7
  143. package/dist/laptop-background.d.ts +16 -0
  144. package/dist/laptop-background.js +108 -2
  145. package/dist/lead-context.js +1 -1
  146. package/dist/lead-contract.d.ts +1 -1
  147. package/dist/lead-contract.js +2 -2
  148. package/dist/lead-progress.js +3 -0
  149. package/dist/lead-status.d.ts +2 -0
  150. package/dist/lead-status.js +6 -2
  151. package/dist/lead-tools.js +51 -2
  152. package/dist/lead.js +1 -1
  153. package/dist/lesson-notes.d.ts +81 -0
  154. package/dist/lesson-notes.js +336 -0
  155. package/dist/limits-ui.js +1 -1
  156. package/dist/local-owner.d.ts +1 -1
  157. package/dist/local-owner.js +11 -0
  158. package/dist/machine-check.d.ts +16 -0
  159. package/dist/machine-check.js +26 -0
  160. package/dist/machine-identity.d.ts +75 -0
  161. package/dist/machine-identity.js +170 -0
  162. package/dist/mcp-core.js +11 -1
  163. package/dist/memory-cli.d.ts +300 -1
  164. package/dist/memory-cli.js +62 -9
  165. package/dist/memory-pass.d.ts +16 -0
  166. package/dist/memory-pass.js +67 -3
  167. package/dist/memory-tidy.d.ts +181 -0
  168. package/dist/memory-tidy.js +523 -0
  169. package/dist/memory-use.d.ts +35 -0
  170. package/dist/memory-use.js +109 -0
  171. package/dist/merge-checklist.d.ts +55 -0
  172. package/dist/merge-checklist.js +182 -0
  173. package/dist/needs-you.d.ts +32 -13
  174. package/dist/needs-you.js +108 -49
  175. package/dist/nightly-github.d.ts +2 -0
  176. package/dist/nightly-github.js +4 -4
  177. package/dist/operate-remote-arguments.js +6 -1
  178. package/dist/operate-remote.js +3 -1
  179. package/dist/operate.d.ts +6 -1
  180. package/dist/operate.js +540 -70
  181. package/dist/phase-routing.js +2 -2
  182. package/dist/plain-schema.d.ts +23 -0
  183. package/dist/plain-schema.js +94 -0
  184. package/dist/plain-words.d.ts +20 -0
  185. package/dist/plain-words.js +66 -0
  186. package/dist/planner.js +2 -1
  187. package/dist/process-liveness.d.ts +2 -0
  188. package/dist/process-liveness.js +61 -17
  189. package/dist/project-delete-ui.d.ts +7 -2
  190. package/dist/project-delete-ui.js +12 -1
  191. package/dist/project-delete.js +13 -2
  192. package/dist/project-knowledge.d.ts +18 -11
  193. package/dist/project-knowledge.js +39 -9
  194. package/dist/project-learning.d.ts +2 -1
  195. package/dist/project-learning.js +15 -45
  196. package/dist/project-memory.d.ts +5 -0
  197. package/dist/project-memory.js +22 -2
  198. package/dist/proof.js +30 -5
  199. package/dist/provider-auth.js +1 -1
  200. package/dist/publish.js +9 -1
  201. package/dist/pull-request-flow.js +5 -0
  202. package/dist/push.d.ts +26 -3
  203. package/dist/push.js +55 -15
  204. package/dist/release-adapters.d.ts +9 -0
  205. package/dist/release-adapters.js +87 -1
  206. package/dist/release-approval.d.ts +191 -0
  207. package/dist/release-approval.js +382 -0
  208. package/dist/release-auto.js +26 -17
  209. package/dist/release-card.d.ts +74 -6
  210. package/dist/release-card.js +179 -23
  211. package/dist/release-carry.d.ts +7 -2
  212. package/dist/release-carry.js +39 -9
  213. package/dist/release-cli.d.ts +4 -0
  214. package/dist/release-cli.js +38 -23
  215. package/dist/release-coverage.js +1 -1
  216. package/dist/release-next.d.ts +48 -0
  217. package/dist/release-next.js +161 -0
  218. package/dist/release.d.ts +50 -1
  219. package/dist/release.js +118 -11
  220. package/dist/remote-destination.d.ts +36 -0
  221. package/dist/remote-destination.js +217 -0
  222. package/dist/remote-main.d.ts +48 -0
  223. package/dist/remote-main.js +84 -5
  224. package/dist/repository-context.d.ts +3 -2
  225. package/dist/repository-context.js +112 -70
  226. package/dist/restart-certification.d.ts +1 -0
  227. package/dist/restart-certification.js +8 -6
  228. package/dist/result-acts.d.ts +29 -21
  229. package/dist/result-acts.js +36 -25
  230. package/dist/result-follow-ups.d.ts +2 -2
  231. package/dist/result-follow-ups.js +18 -4
  232. package/dist/result-review.d.ts +1 -1
  233. package/dist/route-change-check.d.ts +4 -0
  234. package/dist/route-change-check.js +13 -0
  235. package/dist/route-change.d.ts +22 -0
  236. package/dist/route-change.js +89 -0
  237. package/dist/route-hooks.js +3 -2
  238. package/dist/route-results.d.ts +6 -1
  239. package/dist/route-results.js +14 -5
  240. package/dist/run-closeout.d.ts +1 -1
  241. package/dist/run-closeout.js +7 -5
  242. package/dist/runner-process.js +3 -2
  243. package/dist/runner.js +2 -1
  244. package/dist/scope.d.ts +6 -1
  245. package/dist/scope.js +22 -9
  246. package/dist/serve.d.ts +1 -1
  247. package/dist/serve.js +24 -18
  248. package/dist/server/chat.js +14 -12
  249. package/dist/server/chrome.d.ts +1 -1
  250. package/dist/server/chrome.js +26 -35
  251. package/dist/server/console.css +23 -0
  252. package/dist/server/guards.js +4 -0
  253. package/dist/server/home.d.ts +6 -0
  254. package/dist/server/home.js +21 -13
  255. package/dist/server/http.js +1 -1
  256. package/dist/server/kit.js +1 -1
  257. package/dist/server/merge-ready-view.d.ts +10 -0
  258. package/dist/server/merge-ready-view.js +30 -0
  259. package/dist/server/pages-assets.d.ts +1 -0
  260. package/dist/server/pages-assets.js +8 -5
  261. package/dist/server/pages.js +103 -82
  262. package/dist/server/render-chat.js +2 -2
  263. package/dist/server/render-code-map.d.ts +30 -0
  264. package/dist/server/render-code-map.js +137 -0
  265. package/dist/server/render-flaky.d.ts +20 -0
  266. package/dist/server/render-flaky.js +59 -0
  267. package/dist/server/render-history.js +1 -1
  268. package/dist/server/render-new-task.d.ts +26 -1
  269. package/dist/server/render-new-task.js +30 -5
  270. package/dist/server/render-pages.d.ts +1 -175
  271. package/dist/server/render-pages.js +38 -313
  272. package/dist/server/render-projects.d.ts +2 -1
  273. package/dist/server/render-projects.js +9 -7
  274. package/dist/server/render-release.d.ts +19 -0
  275. package/dist/server/render-release.js +84 -0
  276. package/dist/server/render-results.d.ts +2 -2
  277. package/dist/server/render-results.js +71 -73
  278. package/dist/server/render-tasks.d.ts +13 -2
  279. package/dist/server/render-tasks.js +156 -139
  280. package/dist/server/retired-routes.d.ts +17 -0
  281. package/dist/server/retired-routes.js +53 -0
  282. package/dist/server/route-table.d.ts +4 -1
  283. package/dist/server/route-table.js +52 -13
  284. package/dist/server/settings.js +129 -22
  285. package/dist/server/task-views.js +61 -26
  286. package/dist/server/tasks.d.ts +17 -6
  287. package/dist/server/tasks.js +273 -338
  288. package/dist/server/welcome.js +1 -1
  289. package/dist/settings-groups-ui.js +1 -1
  290. package/dist/setup-guide.d.ts +2 -2
  291. package/dist/stop-proof.d.ts +67 -0
  292. package/dist/stop-proof.js +295 -0
  293. package/dist/storage-sweep-process.js +1 -1
  294. package/dist/storage-sweep.d.ts +5 -3
  295. package/dist/storage-sweep.js +30 -9
  296. package/dist/storage-ui.js +28 -17
  297. package/dist/storage.d.ts +27 -2
  298. package/dist/storage.js +39 -2
  299. package/dist/store.d.ts +148 -34
  300. package/dist/store.js +626 -177
  301. package/dist/surface.js +46 -5
  302. package/dist/sweep-quarantine.d.ts +1 -0
  303. package/dist/sweep-quarantine.js +6 -4
  304. package/dist/tamper-scan.d.ts +4 -0
  305. package/dist/tamper-scan.js +90 -0
  306. package/dist/task-aliases.d.ts +5 -0
  307. package/dist/task-aliases.js +9 -0
  308. package/dist/task-control.js +4 -1
  309. package/dist/task-outcome-cli.d.ts +2 -0
  310. package/dist/task-outcome-cli.js +36 -17
  311. package/dist/task-sizing.d.ts +5 -0
  312. package/dist/task-sizing.js +2 -0
  313. package/dist/task-source.d.ts +7 -0
  314. package/dist/task-source.js +14 -0
  315. package/dist/task-status.d.ts +9 -0
  316. package/dist/task-status.js +40 -49
  317. package/dist/teardown-git.d.ts +16 -0
  318. package/dist/teardown-git.js +63 -5
  319. package/dist/telegram-progress.js +5 -3
  320. package/dist/telegram-status.js +6 -4
  321. package/dist/test-infra.d.ts +8 -0
  322. package/dist/test-infra.js +18 -0
  323. package/dist/tidy.js +1 -1
  324. package/dist/toolroll-update-ui.js +2 -2
  325. package/dist/toolroll-update.js +18 -5
  326. package/dist/tui-contract.d.ts +24 -0
  327. package/dist/tui-source.js +67 -8
  328. package/dist/tui.js +51 -51
  329. package/dist/vanished-retry.d.ts +30 -0
  330. package/dist/vanished-retry.js +174 -0
  331. package/dist/work-index.d.ts +2 -0
  332. package/dist/work-index.js +82 -70
  333. package/dist/work-summary.d.ts +2 -2
  334. package/dist/work-summary.js +2 -0
  335. package/dist/workspace-ui.d.ts +4 -2
  336. package/dist/workspace-ui.js +28 -22
  337. package/dist/worktree.d.ts +12 -3
  338. package/dist/worktree.js +68 -13
  339. package/package.json +1 -1
  340. package/dist/server/render-next.d.ts +0 -63
  341. package/dist/server/render-next.js +0 -109
@@ -15,8 +15,8 @@ export function browserWorkActionHref(item) {
15
15
  return '/settings/agents#providers';
16
16
  if (action.code === 'answer-agent' && runId !== null)
17
17
  return `/agents?run=${runId}`;
18
- if (action.code === 'confirm-stopped')
19
- return `/t/${encodeURIComponent(item.rootId)}#confirm-stopped`;
18
+ if (action.code === 'recheck-stopped')
19
+ return `/t/${encodeURIComponent(item.rootId)}#recheck-stopped`;
20
20
  if (action.code === 'open-result' && runId !== null)
21
21
  return chatResultHref(taskId, runId);
22
22
  if (action.code === 'open-pr' && /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/pull\/\d+$/.test(item.publicationUrl ?? ''))
@@ -6,4 +6,4 @@ export declare function serveBrowserAsset(request: IncomingMessage, response: Se
6
6
  /** Retain the complete native document as the no-JavaScript/unloaded-bundle
7
7
  * fallback. React replaces its root only once the local module has loaded.
8
8
  * The data script is escaped for HTML parsing, not merely valid JSON. */
9
- export declare function browserWorkspaceDocument(document: Html, workspace: BrowserWorkspace, nonce: string, functionalScript: string): Html;
9
+ export declare function browserWorkspaceDocument(document: Html, workspace: BrowserWorkspace, nonce: string, functionalScript: string, beside?: Html): Html;
@@ -51,7 +51,7 @@ export function serveBrowserAsset(request, response, path) {
51
51
  /** Retain the complete native document as the no-JavaScript/unloaded-bundle
52
52
  * fallback. React replaces its root only once the local module has loaded.
53
53
  * The data script is escaped for HTML parsing, not merely valid JSON. */
54
- export function browserWorkspaceDocument(document, workspace, nonce, functionalScript) {
54
+ export function browserWorkspaceDocument(document, workspace, nonce, functionalScript, beside = html ``) {
55
55
  const initialize = `window.addEventListener('standing-orders:workspace-rendered',function initializeWorkspace(){window.removeEventListener('standing-orders:workspace-rendered',initializeWorkspace);${functionalScript}});`;
56
56
  // A cross-page fade the browser gives up on rejects its promises; settle them from the head, before the
57
57
  // first frame (the app's own listener arrives too late when a page is revealed before the bundle runs).
@@ -61,5 +61,5 @@ export function browserWorkspaceDocument(document, workspace, nonce, functionalS
61
61
  // A page showing a password keeps to its one script (the sensitivity contract), so it goes without.
62
62
  html `<link rel="stylesheet" href="/assets/workspace.css">${workspace.sensitive ? "" : scriptElement(settleFades, { nonce })}</head>`);
63
63
  out = replaceMarkup(out, /<body>/, () => html `<body><div id="standing-orders-workspace">`);
64
- return replaceMarkup(out, /<\/body>/, () => html `</div>${jsonScript(workspace, { id: "standing-orders-workspace-data", nonce })}${scriptElement(initialize, { nonce })}<script type="module" src="/assets/workspace.js" nonce="${nonce}"></script></body>`);
64
+ return replaceMarkup(out, /<\/body>/, () => html `</div>${beside}${jsonScript(workspace, { id: "standing-orders-workspace-data", nonce })}${scriptElement(initialize, { nonce })}<script type="module" src="/assets/workspace.js" nonce="${nonce}"></script></body>`);
65
65
  }
@@ -259,7 +259,7 @@ export type BrowserTaskRow = {
259
259
  /** Put away, or Restore in Archived (task-tidy.md): a POST to the task's own route; null for a viewer or live work. */
260
260
  tidy?: {
261
261
  action: string;
262
- label: 'Put away' | 'Restore';
262
+ label: 'Archive' | 'Restore';
263
263
  returnTo: string;
264
264
  } | null;
265
265
  };
@@ -381,11 +381,10 @@ export type BrowserTaskView = {
381
381
  statusHtml: string;
382
382
  /** The approval ceremony or its updated terms, exactly as signed. */
383
383
  approval: string;
384
- /** Confirm it stopped, behind the password, when the status asks for it. */
385
- confirmStopped?: {
384
+ /** Check again (a build Toolroll could not prove stopped from this computer), when the status asks for it. */
385
+ recheckStopped?: {
386
386
  action: string;
387
387
  run: number;
388
- checked?: boolean;
389
388
  } | null;
390
389
  /** Build again, in place, when the status asks for it (a result built to an earlier plan). */
391
390
  rebuild?: {
@@ -529,6 +528,8 @@ export type BrowserTaskView = {
529
528
  summary: string;
530
529
  observedAt: string;
531
530
  } | null;
531
+ /** "Can I merge?" (merge-checklist.ts; `toolroll task ready` is its twin): each row and its blocker's form. Null: no result yet. */
532
+ mergeReady?: MergeReadyView | null;
532
533
  /** A release's own steps (release status's twin). Null: this task is no release, or nothing recorded. */
533
534
  release?: {
534
535
  tag: string;
@@ -543,6 +544,13 @@ export type BrowserTaskView = {
543
544
  at: string;
544
545
  } | null;
545
546
  startedAt: string;
547
+ /** The step it is on now, and where that is in its steps. */
548
+ at?: {
549
+ step: string;
550
+ name: string;
551
+ index: number;
552
+ of: number;
553
+ } | null;
546
554
  } | null;
547
555
  /** How the plan was approved: by whom and how, or the rule that approved it automatically. */
548
556
  approved?: {
@@ -555,6 +563,11 @@ export type BrowserTaskView = {
555
563
  } | null;
556
564
  /** ⋯ More actions: the task's secondary acts, the same order on the task and agent pages, destructive last. */
557
565
  more?: BrowserTaskMore[];
566
+ /** The task's full audit record (its link in the header); it moved out of More (docs/plans/plain-toolroll.md P1.3). */
567
+ audit?: {
568
+ label: string;
569
+ href: string;
570
+ } | null;
558
571
  };
559
572
  export type BrowserTaskTab = "activity" | "result" | "plan" | "attempts";
560
573
  /** One ⋯ item: a plain link, a copy of this page's address, a one-click POST (never a ceremony), or a go-to that switches
@@ -703,7 +716,7 @@ export type BrowserResultDecision = {
703
716
  why: string | null;
704
717
  };
705
718
  };
706
- /** A Needs you action: a link, (Confirm it stopped) a form behind the password, (Build again) one
719
+ /** A Needs you action: a link, (Check again, Build again) one
707
720
  * button, or (on the result itself) Accept, which records the person's acceptance; `note` asks why
708
721
  * when the evidence disagrees. */
709
722
  export type BrowserNeedAction = {
@@ -713,7 +726,6 @@ export type BrowserNeedAction = {
713
726
  action: string;
714
727
  run: number;
715
728
  returnTo: string;
716
- checked?: boolean;
717
729
  } | null;
718
730
  accept?: {
719
731
  action: string;
@@ -1661,7 +1673,7 @@ export type BrowserHomeView = {
1661
1673
  href: string;
1662
1674
  }[];
1663
1675
  groups: {
1664
- key: 'decide' | 'review' | 'unblock' | 'running';
1676
+ key: 'decide' | 'review' | 'unblock' | 'running' | 'waiting';
1665
1677
  label: string;
1666
1678
  rows: BrowserHomeRow[];
1667
1679
  }[];
@@ -1855,3 +1867,32 @@ export declare function browserProjectsOf(projects: readonly {
1855
1867
  path: string;
1856
1868
  }[]): BrowserProject[];
1857
1869
  export declare function browserNavigationOf(path: string, project?: string | null, needsYou?: number, tasksProject?: string | null, needsYouLabel?: string): BrowserNavigationItem[];
1870
+ /** One "Can I merge?" act, as the task page's form for it (server/merge-ready-view.ts builds it). */
1871
+ export type MergeReadyAct = {
1872
+ kind: "run-checks";
1873
+ action: string;
1874
+ level: "quick" | "full";
1875
+ returnTo: string;
1876
+ } | {
1877
+ kind: "retry";
1878
+ } | {
1879
+ kind: "fix";
1880
+ action: string;
1881
+ note: string;
1882
+ batch: string;
1883
+ source: string;
1884
+ request: string;
1885
+ returnTo: string;
1886
+ };
1887
+ /** The task page's "Can I merge?" panel. */
1888
+ export type MergeReadyView = {
1889
+ answer: "yes" | "no" | "not-yet" | "merged";
1890
+ sentence: string;
1891
+ rows: {
1892
+ key: string;
1893
+ label: string;
1894
+ mark: "yes" | "no" | "waiting" | "unknown" | "skip";
1895
+ say: string;
1896
+ act: MergeReadyAct | null;
1897
+ }[];
1898
+ };
@@ -90,7 +90,7 @@ export function browserProjectsOf(projects) {
90
90
  if (seen.has(project.path))
91
91
  return [];
92
92
  seen.add(project.path);
93
- return [{ name: project.name, path: project.path, href: `/work?project=${encodeURIComponent(project.path)}`,
93
+ return [{ name: project.name, path: project.path, href: `/tasks?project=${encodeURIComponent(project.path)}`,
94
94
  knowledgeHref: `/settings/knowledge?repo=${encodeURIComponent(project.path)}` }];
95
95
  });
96
96
  }
@@ -102,8 +102,8 @@ export function browserNavigationOf(path, project = null, needsYou = 0, tasksPro
102
102
  return [
103
103
  { label: 'Home', href: '/', active: pathname === '/' || pathname === '/chat' || under('/d', '/activity', '/morning'),
104
104
  ...(needsYou > 0 ? { count: needsYou, ...(needsYouLabel === undefined ? {} : { countLabel: needsYouLabel }) } : {}) },
105
- { label: 'Tasks', href: `/work${tasksProject === null ? '' : `?project=${encodeURIComponent(tasksProject)}`}`,
106
- active: under('/work', '/tasks', '/t', '/r', '/done', '/board', '/queue', '/inbox', '/review') },
105
+ { label: 'Tasks', href: `/tasks${tasksProject === null ? '' : `?project=${encodeURIComponent(tasksProject)}`}`,
106
+ active: under('/tasks', '/t', '/r', '/review') },
107
107
  { label: 'Flows', href: '/flows', active: under('/flows', '/recipes', '/kits', '/routines') },
108
108
  { label: 'Agents', href: '/agents', active: under('/agents', '/peek', '/term', '/fleet', '/system', '/runs') },
109
109
  { label: 'Projects', href: '/projects', active: under('/projects') },
@@ -0,0 +1,21 @@
1
+ import { type Store } from "./store.js";
2
+ /** At most this many automatic restarts in this window (tried, whatever came of them). */
3
+ export declare const RESTART_LIMIT = 3;
4
+ export declare const RESTART_WINDOW_MS: number;
5
+ export type BuilderWish = {
6
+ desired: "running" | "stopped";
7
+ build: string;
8
+ schema: number;
9
+ setBy: string;
10
+ setAt: string;
11
+ };
12
+ export declare function builderWish(store: Store): BuilderWish | null;
13
+ export declare function databaseFormat(store: Store): number | null;
14
+ /** Record what a person asked for. A `stopped` keeps the last known build. */
15
+ export declare function recordBuilderWish(store: Store, desired: "running" | "stopped", by: string, build: string, now: Date): void;
16
+ /** An update, a deploy's drain or a stop in progress: this is someone else's restart to make. */
17
+ export declare function restartPaused(store: Store, now: Date): boolean;
18
+ export type RestartAdmission = "admitted" | "not-wanted" | "paused" | "attempts";
19
+ /** The wish, the pause and the count, read again and the attempt written in one transaction: two callers can't both
20
+ * take the last attempt, and a crash after this still counted. */
21
+ export declare function admitBuilderRestart(store: Store, now: Date): RestartAdmission;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Restarting this computer's own builder (docs/plans/plain-toolroll.md P2.3): the record of what a person wants, and the
3
+ * one transaction that admits an automatic restart. The supervisor work is laptop-background.ts restartOwnBuilder.
4
+ *
5
+ * `background on` and `up` record that the builder should run, with the build and database format they ran; `background
6
+ * off` and `stop` record that it should not, before they stop it. Nothing else writes that wish, and only a recorded
7
+ * wish to run is ever acted on.
8
+ */
9
+ import { updateAdmissionPaused } from "./desktop-update-gate.js";
10
+ import { drainOf } from "./runner.js";
11
+ import { readSchemaVersion } from "./store.js";
12
+ /** At most this many automatic restarts in this window (tried, whatever came of them). */
13
+ export const RESTART_LIMIT = 3;
14
+ export const RESTART_WINDOW_MS = 30 * 60_000;
15
+ export function builderWish(store) {
16
+ const row = store.handle.prepare("SELECT desired, build, schema_version, set_by, set_at FROM builder_service WHERE id = 1").get();
17
+ return row === undefined ? null : { desired: row["desired"] === "running" ? "running" : "stopped", build: String(row["build"]), schema: Number(row["schema_version"]), setBy: String(row["set_by"]), setAt: String(row["set_at"]) };
18
+ }
19
+ export function databaseFormat(store) {
20
+ const read = readSchemaVersion(store.handle);
21
+ return read.ok ? read.version : null;
22
+ }
23
+ /** Record what a person asked for. A `stopped` keeps the last known build. */
24
+ export function recordBuilderWish(store, desired, by, build, now) {
25
+ store.handle.prepare(`INSERT INTO builder_service (id, desired, build, schema_version, set_by, set_at) VALUES (1, ?, ?, ?, ?, ?)
26
+ ON CONFLICT (id) DO UPDATE SET desired = excluded.desired, build = excluded.build, schema_version = excluded.schema_version, set_by = excluded.set_by, set_at = excluded.set_at`)
27
+ .run(desired, build, databaseFormat(store) ?? 0, by, now.toISOString());
28
+ }
29
+ /** An update, a deploy's drain or a stop in progress: this is someone else's restart to make. */
30
+ export function restartPaused(store, now) {
31
+ if (updateAdmissionPaused(store.raw()))
32
+ return true;
33
+ return store.listRunners().some(runner => drainOf(store, runner, now) !== null);
34
+ }
35
+ /** The wish, the pause and the count, read again and the attempt written in one transaction: two callers can't both
36
+ * take the last attempt, and a crash after this still counted. */
37
+ export function admitBuilderRestart(store, now) {
38
+ return store.transact(() => {
39
+ if (builderWish(store)?.desired !== "running")
40
+ return "not-wanted";
41
+ if (restartPaused(store, now))
42
+ return "paused";
43
+ const since = new Date(now.getTime() - RESTART_WINDOW_MS).toISOString();
44
+ const tried = Number(store.handle.prepare("SELECT COUNT(*) AS n FROM builder_restart WHERE at > ?").get(since)?.["n"] ?? 0);
45
+ if (tried >= RESTART_LIMIT)
46
+ return "attempts";
47
+ store.handle.prepare("INSERT INTO builder_restart (at) VALUES (?)").run(now.toISOString());
48
+ return "admitted";
49
+ });
50
+ }
package/dist/builder.d.ts CHANGED
@@ -3,6 +3,7 @@ import { type CheckAccess } from "./check-access.js";
3
3
  import type { WritesMode } from "./writes-mode.js";
4
4
  import type { LaunchNetwork } from "./invoke.js";
5
5
  import type { ProviderId } from "./contracts/provider.js";
6
+ import { type MachineSample } from "./check-load.js";
6
7
  import { type PreparedEvidence } from "./prepared-evidence.js";
7
8
  import { type ExecResult, type RunOptions } from "./exec.js";
8
9
  import type { Decision, Store } from "./store.js";
@@ -121,6 +122,9 @@ export type BuildRequest = {
121
122
  /** Runs the repository's approved verification command (Priority 2), after
122
123
  * commit. Tests inject; production uses exec. */
123
124
  verify?: Runner;
125
+ /** Reads the machine's cores and load for a release check's limit and load record (check-load.ts). Tests inject;
126
+ * production reads the operating system. */
127
+ machine?: () => MachineSample;
124
128
  /**
125
129
  * The stop fence (audit IV-1, completed): re-proved AFTER the agent and
126
130
  * BEFORE the commit. A stop that lands while the agent runs preserves
package/dist/builder.js CHANGED
@@ -1,15 +1,17 @@
1
1
  import { legacyNetwork, repositoryEcosystems } from "./egress.js";
2
2
  import { CHECK_ACCESS_DEFAULT, checkAccessHere, checkNetworkOf, localModeCap } from "./check-access.js";
3
3
  import { localModeOn } from "./local-owner.js";
4
- import { checkoutGit } from "./teardown-git.js";
4
+ import { checkoutGit, refusal as gitRefusal } from "./teardown-git.js";
5
5
  import { isAcpProvider } from "./acp-agents.js";
6
6
  import { OBSERVATION_MAILBOX, observationBrief, parseObservationCases, collectObservations } from "./observations.js";
7
7
  import { checkCommandFor, effectiveCheckLevel, recordRunCheckLevel } from "./check-levels.js";
8
+ import { captureCheckConditions, learnRequestFor, recordCheckFacts } from "./check-facts.js";
8
9
  import { queueBatchCheck } from "./batch-checks.js";
9
10
  import { batchesFullCheck } from "./batch-policy.js";
10
11
  import { skillsContext } from "./project-skills.js";
11
12
  import { failedVerificationEvidence, sealVerificationReceipt, verificationEvidence, reuseObservationVerification } from "./verification-evidence.js";
12
- import { CHECK_INVENTORY_CAPTURE, CHECK_REPORT_CAPTURE, CheckBaselineUnverifiable, prepareCheckAttempt, reportFromOutput, sealCheckOutcome } from "./check-report.js";
13
+ import { CHECK_INVENTORY_CAPTURE, CHECK_REPORT_CAPTURE, CheckBaselineUnverifiable, prepareCheckAttempt, reportFromOutput, sealCheckOutcome, sealJson } from "./check-report.js";
14
+ import { CHECK_LOAD_CAPTURE, CheckLoadWatch } from "./check-load.js";
13
15
  import { retriedInside } from "./check-failure.js";
14
16
  import { planTargetedRecheck, settleCoverage } from "./release-recheck.js";
15
17
  import { effectiveCheckFacts } from "./verification-evidence.js";
@@ -79,6 +81,7 @@ import { LIVE_TICK_MS, tickLiveSteer } from "./steer-live.js";
79
81
  import { withRunActivity } from "./task-activity.js";
80
82
  import { CheckProgressTracker, coalescedProgressWriter } from "./check-progress.js";
81
83
  import { captureParkEvidence, captureTerminalDiff, captureBaseTree, evidenceRoot, handoffName, isImagePath, WORKTREE_EVIDENCE_DIR, storeHandoffArtifact, looksLikeProtocolFile, mailboxName, progressFileName, steerFolderName, statusFileName, proofFileName, rubricFileName, proposalFileName, quarantineMailboxes, readMailbox, readVerifiedArtifact, scanForSecrets, redactSecretLines, storeEvidence, validateScreenshotBytes, imageDimensions, SCREENSHOT_BYTE_CAP, boundStreamHeadTail } from "./evidence.js";
84
+ import { deliverCodeMap } from "./code-map-admission.js";
82
85
  import { attemptKeyOf, closeRunQuestions, noteOpenTool, recordAgentTurn } from "./agent-board.js";
83
86
  import { openStatusFile, pulseAgentBoard, removeStatusFile, statusBrief } from "./agent-pulse.js";
84
87
  import { withAgentTurns } from "./agent-turns.js";
@@ -706,8 +709,11 @@ export async function build(store, request) {
706
709
  return { ok: false, reason: "no-op", message: refusal };
707
710
  // Check the exact candidate before setup or the full gate spends anything.
708
711
  const listed = await git(GIT, [...preparedEvidenceGit(), "ls-tree", "-z", preparedCandidate, "--", PREPARED_EVIDENCE_FILE], { cwd: worktree, maxBuffer: 2048 });
709
- if (listed.code !== 0)
710
- return { ok: false, reason: "no-op", message: "The committed screenshot inventory could not be inspected. Restore the saved candidate before review." };
712
+ if (listed.code !== 0) {
713
+ // Say why (runs 3316–3324 said only this sentence while the real cause was a refused config read).
714
+ const why = gitRefusal(listed) ?? listed.stderr.split("\n").map(line => line.trim()).find(line => line !== "") ?? `git exited ${listed.code}`;
715
+ return { ok: false, reason: "no-op", message: `The committed screenshot inventory could not be inspected (${redactSecretText(why).slice(0, 400)}). Restore the saved candidate before review.` };
716
+ }
711
717
  const required = scope?.acceptance.some(criterion => criterion.evidence.includes("screenshot")) ?? false;
712
718
  try {
713
719
  if (required || listed.stdout.length > 0)
@@ -1312,7 +1318,7 @@ export async function build(store, request) {
1312
1318
  const retryBase = pinnedBase !== null && pinnedBase !== baseRevision ? pinnedBase : null;
1313
1319
  const contextBase = codingHandoff !== null ? baseRevision : undefined;
1314
1320
  const lessonContext = learningContext(store, root, request.runId, "build", clock(), contextBase);
1315
- const briefText = projectSkillContext + knowledgeContext(store, request.runId, join(root, '..', 'repository-context'), contextBase) + lessonContext + brief(scope, branch, mailbox, done, proof, answers, planDocument, revisionBrief, previousHandoff, steering, retryBase, request.recoveredDraftRun ?? null, request.recoveredDraftKind ?? "partial",
1321
+ const briefText = projectSkillContext + knowledgeContext(store, request.runId, join(root, '..', 'repository-context'), contextBase, clock()) + deliverCodeMap(store, request.runId, clock()) + lessonContext + brief(scope, branch, mailbox, done, proof, answers, planDocument, revisionBrief, previousHandoff, steering, retryBase, request.recoveredDraftRun ?? null, request.recoveredDraftKind ?? "partial",
1316
1322
  // The adaptive-execution-plan protocol is offered ONLY when there is a
1317
1323
  // real plan with real milestones to checkpoint against: no plan means
1318
1324
  // no revision to name, no ids to report, and nothing to propose a
@@ -2322,11 +2328,16 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2322
2328
  const gateCommand = batched ? null : configured;
2323
2329
  let verifyCommand;
2324
2330
  let checkAttempt = null;
2331
+ // A release check's limit and the machine's load while it runs, measured by the plane itself (check-load.ts).
2332
+ let loadWatch = null;
2325
2333
  let releaseFacts = null;
2326
2334
  const verification = { last: null };
2335
+ // What the check ran under, captured as its command starts (check-facts.ts): advisory, recorded at disposition.
2336
+ const checkConditions = { last: null };
2327
2337
  const checkLog = [];
2328
2338
  const checkOutcomes = [];
2329
2339
  const checkSuites = [];
2340
+ let settledReport = null;
2330
2341
  const noteCheckSuite = (name, result) => {
2331
2342
  checkSuites.push({
2332
2343
  name,
@@ -2380,9 +2391,13 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2380
2391
  // A release's check (signed terms or an open authority): CI never stands in for its unit suite.
2381
2392
  // A release's check: signed terms, a release check purpose (pipeline-speed.md, P0) or an open authority.
2382
2393
  const release = decision.authority !== null || releaseCheckScope(store.getScope(request.taskId));
2394
+ // A busy machine cuts the check's budget; the plane gives it time in proportion, bounded (check-load.ts).
2395
+ loadWatch = new CheckLoadWatch(configured.timeoutMs, request.machine === undefined ? {} : { sample: request.machine });
2383
2396
  try {
2384
- checkAttempt = prepareCheckAttempt(store, root, { runId, repo, head: sealedHead, command: configured, access, authority: decision.authority,
2385
- ...(decision.mode === "targeted" ? { plan: decision.plan, snapshot: decision.snapshot } : {}), watch: decision.watch, release }, now());
2397
+ checkAttempt = prepareCheckAttempt(store, root, { runId, repo, head: sealedHead, command: configured, access, authority: decision.authority, limitMs: loadWatch?.limitMs,
2398
+ ...(decision.mode === "targeted" ? { plan: decision.plan, snapshot: decision.snapshot } : {}), watch: decision.watch, release,
2399
+ // Advisory only (docs/plans/learning-loops.md): the project's tracked identities, a separate handshake field.
2400
+ learn: learnRequestFor(store, repo, now(), runId).learn }, now());
2386
2401
  }
2387
2402
  catch (error) {
2388
2403
  // A recorded last release that can't be verified (eval-harness review 2, finding 4): nothing runs.
@@ -2392,6 +2407,12 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2392
2407
  baselineUnverifiable = error.message;
2393
2408
  recordCheckNote(error.message);
2394
2409
  }
2410
+ if (checkAttempt === null && loadWatch !== null) {
2411
+ loadWatch.finish();
2412
+ loadWatch = null;
2413
+ }
2414
+ if (loadWatch !== null && loadWatch.limitMs !== configured.timeoutMs)
2415
+ store.addRunNote(runId, "Toolroll", `The release check's time limit: ${loadWatch.why}.`, now());
2395
2416
  if (checkAttempt !== null && decision.mode === "targeted")
2396
2417
  store.addRunNote(runId, "Toolroll", `Re-running only what failed at run #${decision.plan.base}: ${decision.plan.units.length} unit files, ${decision.plan.groups.reduce((n, one) => n + one.groups.length, 0)} journey groups, ${decision.plan.versions.length} upgrade versions${decision.plan.parts.length > 0 ? `, and in full: ${decision.plan.parts.join(", ")}` : ""}.`, now());
2397
2418
  }
@@ -2404,26 +2425,32 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2404
2425
  const checkProgress = new CheckProgressTracker(snapshot => progressWrites.push(snapshot));
2405
2426
  const runVerification = async (label, missing = null) => {
2406
2427
  const streamed = { stdout: false, stderr: false };
2428
+ // The conditions it starts under (verified tree, command, access, inherited environment): advisory, never authority.
2429
+ if (missing === null)
2430
+ checkConditions.last = captureCheckConditions({ repo, head: sealedHead, command: configured, access: checkLaunchOf(store, runId, captured.effective.profile).access, envNames: SETUP_ENV_ALLOWLIST });
2407
2431
  // The check runs under the stop watch (v52), owned by this run.
2408
2432
  const result = missing !== null
2409
2433
  ? { code: 127, stdout: "", stderr: `Toolroll did not start the check: ${missing} wasn't found.`, timedOut: false, notFound: false }
2410
- : await underStopWatch(store, runId, () => runWithIsolatedDatabase(witnessedRunner(store, runId, now, commandRunner(store, runId, captured.effective.profile, verifyRunner)), verifyShell.file, verifyShell.args, {
2411
- cwd: worktree,
2412
- timeoutMs: configured.timeoutMs,
2413
- processGroup: true,
2414
- owner: runOwnerTag(store, runId),
2415
- beforeSpawn: () => !stopRequestedFor(store, runId, request.shouldStop),
2416
- onSpawn: pid => {
2417
- request.onProviderSpawn?.(pid);
2418
- if (stopRequestedFor(store, runId, request.shouldStop))
2419
- throw new Error("the attempt was stopped before spawn custody completed");
2420
- },
2421
- envAllowlist: SETUP_ENV_ALLOWLIST,
2422
- omitEnv: SETUP_ENV_DENYLIST,
2423
- ...(checkAttempt === null ? {} : { stdin: checkAttempt.stdin }),
2424
- onStdout: chunk => { streamed.stdout = true; checkProgress.feed(chunk, "stdout"); },
2425
- onStderr: chunk => { streamed.stderr = true; checkProgress.feed(chunk, "stderr"); },
2426
- }));
2434
+ : await underStopWatch(store, runId, () => {
2435
+ loadWatch?.attemptStarted();
2436
+ return runWithIsolatedDatabase(witnessedRunner(store, runId, now, commandRunner(store, runId, captured.effective.profile, verifyRunner)), verifyShell.file, verifyShell.args, {
2437
+ cwd: worktree,
2438
+ timeoutMs: loadWatch?.limitMs ?? configured.timeoutMs,
2439
+ processGroup: true,
2440
+ owner: runOwnerTag(store, runId),
2441
+ beforeSpawn: () => !stopRequestedFor(store, runId, request.shouldStop),
2442
+ onSpawn: pid => {
2443
+ request.onProviderSpawn?.(pid);
2444
+ if (stopRequestedFor(store, runId, request.shouldStop))
2445
+ throw new Error("the attempt was stopped before spawn custody completed");
2446
+ },
2447
+ envAllowlist: SETUP_ENV_ALLOWLIST,
2448
+ omitEnv: SETUP_ENV_DENYLIST,
2449
+ ...(checkAttempt === null ? {} : { stdin: checkAttempt.stdin }),
2450
+ onStdout: chunk => { streamed.stdout = true; checkProgress.feed(chunk, "stdout"); },
2451
+ onStderr: chunk => { streamed.stderr = true; checkProgress.feed(chunk, "stderr"); },
2452
+ }).finally(() => loadWatch?.attemptEnded());
2453
+ });
2427
2454
  if (missing !== null)
2428
2455
  unstartedChecks.set(result, missing);
2429
2456
  if (!streamed.stdout)
@@ -2618,13 +2645,24 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2618
2645
  checkProgress.finish();
2619
2646
  }
2620
2647
  catch { /* display only */ }
2648
+ // The plane's own load record (check-load.ts), sealed beside the receipt: the one thing that can call a timeout on a
2649
+ // busy machine the machine's own fault. A record that can't be sealed excuses nothing; the check's outcome is unchanged.
2650
+ if (loadWatch !== null) {
2651
+ const record = loadWatch.finish();
2652
+ try {
2653
+ sealJson(store, root, runId, "check-load.json", record, CHECK_LOAD_CAPTURE, now());
2654
+ }
2655
+ catch (error) {
2656
+ store.addRunNote(runId, "Toolroll", `This check's load record wasn't kept (${redactSecretText(error instanceof Error ? error.message : String(error))}), so a timeout in it can't be called the machine's own.`, now());
2657
+ }
2658
+ }
2621
2659
  if (!progressWrites.flush())
2622
2660
  progressWrites.flush();
2623
2661
  // The check's own report (the last line carrying the nonce), sealed beside what was sealed before it ran.
2624
2662
  const last = verification.last;
2625
2663
  let sealedReport = null;
2626
2664
  if (checkAttempt !== null && last !== null && !last.notFound) {
2627
- sealedReport = sealCheckOutcome(store, root, checkAttempt, { stdout: last.stdout, exitCode: last.timedOut ? null : last.code }, now());
2665
+ sealedReport = settledReport = sealCheckOutcome(store, root, checkAttempt, { stdout: last.stdout, exitCode: last.timedOut ? null : last.code }, now());
2628
2666
  // Where the check's time went (latency-timeline.ts): each part's start and end, display only.
2629
2667
  keepCheckTimeline(store, { runId, taskId: request.taskId, report: sealedReport, stdout: last.stdout, now: now() });
2630
2668
  }
@@ -2683,6 +2721,16 @@ async function settleProof(captured, statArtifactId, sealedHead) {
2683
2721
  exitCode: checkExitCode,
2684
2722
  suites,
2685
2723
  }, now());
2724
+ // What the check ran under and reported, every failure in it, each tagged by source (check-facts.ts): advisory, never
2725
+ // read for this outcome, a verdict or an approval.
2726
+ // A failure to record them is said on the run and never reaches the check's outcome.
2727
+ try {
2728
+ recordCheckFacts(store, root, { runId, taskRef: captured.taskRef, repo, conditions: checkConditions.last, receipt: { status: checkStatus }, report: settledReport,
2729
+ attempt: checkAttempt === null ? null : { nonce: checkAttempt.nonce, learn: checkAttempt.learn }, stdout: verification.last?.stdout ?? null, now: now() });
2730
+ }
2731
+ catch (error) {
2732
+ store.addRunNote(runId, "Toolroll", `This check's facts for the flaky-test list weren't recorded (${redactSecretText(error instanceof Error ? error.message : String(error))}); its result is unchanged.`, now());
2733
+ }
2686
2734
  // 4. The sealed diff-stat was restated above, before the correction; it
2687
2735
  // is re-read now, after the gate, and the cached facts are adjudicated
2688
2736
  // only when the artifact still states exactly them. The gate receipt
@@ -14,7 +14,7 @@ export function acceptanceStatus(proof, accepted, check) {
14
14
  if (proof?.verdict === "verified" && check !== "passed") {
15
15
  return check === undefined ? "Open the result to see its checks" : check === "failed" ? "Checks failed" : check === "running" ? "Checks running" : "The check result couldn't be read";
16
16
  }
17
- return proof === null ? "No checks recorded" : ({ verified: "Checks passed", attested: "Checks reported by the agent", short: "Some required material is missing", refuted: "Saved result conflicts with the approved scope" }[proof.verdict] ?? "Some required material is missing");
17
+ return proof === null ? "No checks recorded" : ({ verified: "Checks passed", attested: "Checks reported by the agent", short: "Some required material is missing", refuted: "Saved result doesn't match the approved plan" }[proof.verdict] ?? "Some required material is missing");
18
18
  }
19
19
  export function readAcceptanceEvidence(store, who, root, task, runId, offset = 0) {
20
20
  const ref = store.lookupRef(task);
@@ -239,6 +239,16 @@ export declare const CHAT_ACTIONS: {
239
239
  readonly protected: false;
240
240
  readonly password: false;
241
241
  };
242
+ readonly code_map_switch: {
243
+ readonly label: "Change code map";
244
+ readonly protected: false;
245
+ readonly password: false;
246
+ };
247
+ readonly code_map_holdout_end: {
248
+ readonly label: "End holdout";
249
+ readonly protected: false;
250
+ readonly password: false;
251
+ };
242
252
  readonly lead_about_you: {
243
253
  readonly label: "Remember";
244
254
  readonly protected: false;
@@ -250,7 +260,7 @@ export declare const CHAT_ACTIONS: {
250
260
  readonly password: true;
251
261
  };
252
262
  readonly result_accept: {
253
- readonly label: "Accept and finish";
263
+ readonly label: "Accept";
254
264
  readonly protected: true;
255
265
  readonly password: false;
256
266
  };
@@ -287,7 +297,7 @@ export declare function sharedActionReviewPath(id: number): string;
287
297
  export declare function mintSharedActionReview(store: Store, who: VerifiedApprover, id: number, root: string, now: Date): {
288
298
  payload: {
289
299
  version: 1;
290
- operation: "decision_record" | "decision_retire" | "flow_browser" | "flow_browser_off" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "subagent_answer" | "subagent_ask" | "subagent_create" | "subagent_memory" | "subagent_note" | "subagent_routine" | "subagent_soul" | "subagent_state" | "subagent_tools" | "subagent_undo" | "suggestion_not_now" | "sweep_cancel_rest" | "sweep_file" | "task_cancel" | "task_resume" | "tool_add" | "tool_remove" | "trial_stop";
300
+ operation: "code_map_holdout_end" | "code_map_switch" | "decision_record" | "decision_retire" | "flow_browser" | "flow_browser_off" | "flow_card_add" | "flow_card_approve" | "flow_card_assign" | "flow_card_cancel" | "flow_card_choose" | "flow_card_comment" | "flow_card_move" | "flow_card_send_back" | "flow_card_watch" | "flow_create" | "flow_edit" | "flow_script_save" | "flow_starter" | "flow_trigger_add" | "flow_trigger_pause" | "flow_trigger_remove" | "flow_trigger_resume" | "kit_setup" | "knowledge_instructions" | "knowledge_remove" | "knowledge_restore" | "knowledge_save" | "lead_about_you" | "result_accept" | "scope_approve" | "skill_disable" | "skill_enable" | "skill_import" | "skill_restore" | "skill_test" | "subagent_answer" | "subagent_ask" | "subagent_create" | "subagent_memory" | "subagent_note" | "subagent_routine" | "subagent_soul" | "subagent_state" | "subagent_tools" | "subagent_undo" | "suggestion_not_now" | "sweep_cancel_rest" | "sweep_file" | "task_cancel" | "task_resume" | "tool_add" | "tool_remove" | "trial_stop";
291
301
  request: {
292
302
  [x: string]: unknown;
293
303
  };
@@ -1,5 +1,7 @@
1
1
  /** Shared, exact-state actions. Models may prepare; only the existing human
2
2
  * confirmation door executes. Protected actions use a one-use review receipt. */
3
+ import { endHoldout, setCodeMapSwitch } from "./code-map-acts.js";
4
+ import { codeMapOn } from "./code-map-store.js";
3
5
  import { dismissSuggestion, ruleById, stopTrial, suggestionsFor, suggestionWords, taskEvidenceWords, trialWords } from "./route-rules.js";
4
6
  import { CHAT_ACTION_LOCAL_ACTS, currentLocalOwner } from "./local-owner.js";
5
7
  import { fileTaskProposal } from "./proposal.js";
@@ -133,10 +135,13 @@ export const CHAT_ACTIONS = {
133
135
  suggestion_not_now: { label: "Not now", protected: false, password: false },
134
136
  // Stopping a trial only lowers: new tasks build on the configured model; approved ones keep theirs.
135
137
  trial_stop: { label: "Stop trial", protected: false, password: false },
138
+ // The code map's switch and holdout (docs/plans/code-map.md §5): a person's yes on the card, re-proved in the write.
139
+ code_map_switch: { label: "Change code map", protected: false, password: false },
140
+ code_map_holdout_end: { label: "End holdout", protected: false, password: false },
136
141
  // What the owner's own lead knows about them: theirs alone, in no project (lead-about.ts).
137
142
  lead_about_you: { label: "Remember", protected: false, password: false },
138
143
  scope_approve: { label: "Approve work", protected: true, password: true },
139
- result_accept: { label: "Accept and finish", protected: true, password: false },
144
+ result_accept: { label: "Accept", protected: true, password: false },
140
145
  task_cancel: { label: "Cancel task", protected: true, password: false },
141
146
  task_resume: { label: "Resume task", protected: true, password: true },
142
147
  };
@@ -284,7 +289,7 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
284
289
  const stamp = hash({ operation, request, repo: "", state, terms, actor: who.name, generation: who.generation, ceiling: who.ceilingDigest });
285
290
  return { version: SHARED_ACTION_VERSION, operation, request, repo: "", title, terms, stamp, state };
286
291
  }
287
- const task = operation.startsWith("skill_") || operation.startsWith("knowledge_") || operation.startsWith("decision_") || operation.startsWith("tool_") || operation.startsWith("flow_") || operation.startsWith("subagent_") || operation.startsWith("kit_") || operation.startsWith("sweep_") || operation.startsWith("suggestion_") || operation.startsWith("trial_")
292
+ const task = operation.startsWith("code_map_") || operation.startsWith("skill_") || operation.startsWith("knowledge_") || operation.startsWith("decision_") || operation.startsWith("tool_") || operation.startsWith("flow_") || operation.startsWith("subagent_") || operation.startsWith("kit_") || operation.startsWith("sweep_") || operation.startsWith("suggestion_") || operation.startsWith("trial_")
288
293
  ? null
289
294
  : text(input, "task", 64);
290
295
  const flowTarget = operation.startsWith("flow_") && operation !== "flow_create" && operation !== "flow_script_save" && operation !== "flow_starter" ? flowTargetOf(store, input) : null;
@@ -891,6 +896,17 @@ export function prepareSharedAction(store, who, operation, input, root, now = ne
891
896
  title = "Stop trying a cheaper model";
892
897
  terms.push(trialWords(store, trial), "New tasks build on the configured model again. Tasks a person already approved on the trial keep that model.");
893
898
  }
899
+ else if (operation === "code_map_switch") {
900
+ const on = input["on"] === true;
901
+ state = { on: codeMapOn(store, repo) };
902
+ title = on ? "Turn the code map on" : "Turn the code map off";
903
+ terms.push(on ? "Toolroll keeps a map of main for this project, and builds get it." : "No new maps are made, and builds get none. The last map stays saved.");
904
+ }
905
+ else if (operation === "code_map_holdout_end") {
906
+ state = { holdout: true };
907
+ title = "End the code map holdout";
908
+ terms.push("No new builds are enrolled. Builds already enrolled keep their arm, so the results stay comparable.");
909
+ }
894
910
  else if (operation.startsWith("decision_")) {
895
911
  // The staleness fence: the newest decision id and the active count, so a
896
912
  // card drafted before a subagent recorded or retired one is refused.
@@ -1282,6 +1298,13 @@ export function executeSharedAction(store, who, id, payload, now, options) {
1282
1298
  throw Error(done.problem);
1283
1299
  return { ok: true, taskId: null, said: "Trial stopped. New tasks build on the configured model.", href: `/settings/models?scope=${encodeURIComponent(repo)}#models-suggested` };
1284
1300
  }
1301
+ else if (payload.operation === "code_map_switch" || payload.operation === "code_map_holdout_end") {
1302
+ const door = currentLocalOwner() !== null ? { kind: "local-owner", actor } : { kind: "person", person: { actor: who.name, generation: who.generation, method: "session" } };
1303
+ const done = payload.operation === "code_map_switch" ? setCodeMapSwitch(store, door, repo, req["on"] === true, now) : endHoldout(store, door, repo, now);
1304
+ if (!done.ok)
1305
+ throw Error(done.problem);
1306
+ return { ok: true, taskId: null, said: payload.operation === "code_map_switch" ? `The code map is ${req["on"] === true ? "on" : "off"}.` : "The holdout has ended.", href: `/settings/project?repo=${encodeURIComponent(repo)}#code-map` };
1307
+ }
1285
1308
  else if (payload.operation === "suggestion_not_now") {
1286
1309
  // The rule door (route-store.ts proveRuleDoor) re-proves who says it: this computer's local owner only inside
1287
1310
  // its `dismiss-suggestion` act, or the confirming person.
@@ -529,6 +529,16 @@ export const CHAT_ACTION_PARITY = {
529
529
  how: "Reads bounded source excerpts or advisory import impact in an accessible enrolled project through the shared engine, with source-search fallback when its index is unavailable.",
530
530
  gap: "Index refresh uses the local CLI. Dedicated Telegram and Slack journeys for this tool and live channel rendering remain unverified.",
531
531
  },
532
+ get_code_map: {
533
+ support: "direct",
534
+ how: "Reads the project's generated code map through the shared reader, one labelled answer at most 6 KB, paging one pinned version.",
535
+ gap: null,
536
+ },
537
+ refresh_code_map: {
538
+ support: "direct",
539
+ how: "Queues a check of the project's main, as Check now does on the page.",
540
+ gap: null,
541
+ },
532
542
  get_action_status: {
533
543
  support: "direct",
534
544
  how: "Reads the exact saved shared action and its outcome, including completion through secure review.",
@@ -569,6 +579,7 @@ export const CHAT_ACTION_PARITY = {
569
579
  how: "Read during a turn.",
570
580
  gap: null,
571
581
  },
582
+ get_flaky_tests: { support: "direct", how: "Read during a turn: a project's tests with mixed outcomes, each with the task that would fix it. Fixing one is an ordinary task card.", gap: null },
572
583
  get_models: { support: "direct", how: "Read during a turn: default agents with the exact model each runs, CLI versions and new models. Changes use the labelled Settings → Models page.", gap: null },
573
584
  get_diff: { support: "direct", how: "Read during a turn: the exact result's changed files, then one file's diff, so a requested change names the right file and line.", gap: null },
574
585
  get_check_log: { support: "direct", how: "Read during a turn: the end of the exact result's check log, a page of it, or the lines matching a search.", gap: null },
@@ -51,13 +51,18 @@ export type DecideButton = {
51
51
  };
52
52
  /** The words, said once. */
53
53
  export declare const DECIDE_LABELS: {
54
- readonly accept: "Accept and finish";
55
- readonly changes: "Request changes";
54
+ readonly accept: "Accept";
55
+ readonly changes: "Send back";
56
56
  readonly retry: "Retry";
57
57
  readonly approve: "Approve & start";
58
58
  readonly "not-now": "Not now";
59
59
  readonly merge: "Merge";
60
60
  readonly look: "Look first";
61
+ /** A result's one link (docs/plans/plain-toolroll.md P1.5). */
62
+ readonly open: "Open result";
63
+ /** A plan card's one button: it shows the full terms, and the Approve sits under them. Never an approval itself. */
64
+ readonly review: "Review plan";
65
+ readonly details: "Details";
61
66
  readonly edit: "Edit";
62
67
  readonly yes: "Yes";
63
68
  readonly cancel: "Cancel";