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
@@ -1,5 +1,6 @@
1
1
  /** Task pages: approval, agents, plans, controls, decisions and the task body. */
2
2
  import { archivedWords } from "../tidy.js";
3
+ import { btn } from "./kit.js";
3
4
  import { whenUtc } from "../when-html.js";
4
5
  import { networkWords } from "../egress.js";
5
6
  import { releaseAsksAgainWords, releaseTermsSentence } from "../scope.js";
@@ -28,6 +29,7 @@ import { starterForWork } from "../flow-starters.js";
28
29
  import {} from "../gaps.js";
29
30
  import { readAuthModeStrict } from "../keys.js";
30
31
  import { failedAttemptSentence, retryNoteOf, START_BUILDER_COMMAND, startBuilderHref } from "../needs-you.js";
32
+ import { planVersionWords } from "../plain-words.js";
31
33
  import { chosenWords, makesNoPlan, PHASES as ROUTE_PHASES, TASK_SIZES } from "../phase-routing.js";
32
34
  import { parseExecutionPlanDocument } from "../plan.js";
33
35
  import {} from "../planner-source.js";
@@ -50,7 +52,7 @@ import { isOverdue, oneLineOf, projectChip, regionScript, safePrUrl, screen, SEN
50
52
  import { steerDestinationWords, steerStandingParts } from "../steer-status.js";
51
53
  import { reviewHref, taskChatHref, taskHref } from "./http.js";
52
54
  import { coordinatorProposalsSection } from "./render-chat.js";
53
- import { completionReceiptCard, criterionMatrixHtml, evidenceWords, homePhaseWords, incidentWords, needsCheck, pullRequestCardHtml, reasonWords, receiptStatusOf, repairChainHtml, reportItemsHtml, reviewRetryPanel, runNoun, runOutcomeBadge, semanticCoverageHtml, verificationExplanation, verificationRecovered } from "./render-results.js";
55
+ import { completionReceiptCard, criterionMatrixHtml, evidenceWords, homePhaseWords, incidentWords, pullRequestCardHtml, reasonWords, receiptStatusOf, repairChainHtml, reportItemsHtml, reviewRetryPanel, runNoun, runOutcomeBadge, semanticCoverageHtml, verificationExplanation, verificationRecovered } from "./render-results.js";
54
56
  import {} from "./session.js";
55
57
  import { linkWords, originNote, requestName, requestWords } from "../task-source.js";
56
58
  export function contractChangeHtml(change) {
@@ -61,10 +63,10 @@ export function contractChangeHtml(change) {
61
63
  case "outOfScope":
62
64
  return html `<li>${tag} <strong>not this</strong>${change.before === null ? "" : html `<div class="contract-before">was: ${change.before}</div>`}${change.after === null ? html `<div class="contract-after">now: <em>no exclusions</em></div>` : html `<div class="contract-after">now: ${change.after}</div>`}</li>`;
63
65
  case "touches":
64
- return html `<li>${tag} <strong>touches</strong> <span class="mono">${change.path}</span></li>`;
66
+ return html `<li>${tag} <strong>files</strong> <span class="mono">${change.path}</span></li>`;
65
67
  case "acceptance": {
66
68
  const criterion = (one) => one === null ? "" : html `${one.statement} <span class="meta">[requires: ${one.evidence.join(", ")}]</span>${one.how === null ? "" : html `<div class="meta">how: ${one.how}</div>`}`;
67
- return html `<li>${tag} <strong>criterion <code>${change.id}</code></strong>${change.kind === "changed" ? html ` <span class="meta">(${change.moved.join(", ")})</span>` : ""}${change.before === null ? "" : html `<div class="contract-before">${change.kind === "removed" ? "removed: " : "was: "}${criterion(change.before)}</div>`}${change.after === null ? "" : html `<div class="contract-after">${change.kind === "added" ? "added: " : "now: "}${criterion(change.after)}</div>`}</li>`;
69
+ return html `<li>${tag} <strong>requirement <code>${change.id}</code></strong>${change.kind === "changed" ? html ` <span class="meta">(${change.moved.join(", ")})</span>` : ""}${change.before === null ? "" : html `<div class="contract-before">${change.kind === "removed" ? "removed: " : "was: "}${criterion(change.before)}</div>`}${change.after === null ? "" : html `<div class="contract-after">${change.kind === "added" ? "added: " : "now: "}${criterion(change.after)}</div>`}</li>`;
68
70
  }
69
71
  }
70
72
  }
@@ -78,25 +80,25 @@ export function contractChangeHtml(change) {
78
80
  */
79
81
  export function planContractHtml(view, mode) {
80
82
  if (view === null) {
81
- return mode === "full" ? html `` : html `<p class="meta contract-note">no contract record for this draft — compare the scope above against what you filed before signing</p>`;
83
+ return mode === "full" ? html `` : html `<p class="meta contract-note">No record compares this plan with your request. Check the plan above against what you asked for before approving.</p>`;
82
84
  }
83
85
  if ("problem" in view) {
84
- return html `<div class="contract-panel contract-problem"><p class="approval-label">filed contract</p><p class="meta">${view.problem} · <a href="/r/${view.run}">run ${view.run}</a></p></div>`;
86
+ return html `<div class="contract-panel contract-problem"><p class="approval-label">Your request</p><p class="meta">${view.problem} · <a href="/r/${view.run}">Planner's record</a></p></div>`;
85
87
  }
86
- const stale = view.current ? "" : html `<p class="meta">The scope was edited after this draft landed — the record below describes the draft as the planner proposed it</p>`;
88
+ const stale = view.current ? "" : html `<p class="meta">The plan was edited after the planner drafted it. The record below is the planner's draft.</p>`;
87
89
  if (!view.filed) {
88
90
  return mode === "full"
89
- ? html `<div class="contract-panel contract-drafted"><p class="approval-label">filed contract</p><p class="meta">No scope was filed before planning — the planner drafted this contract from the title and the repository; review every term as new</p>${stale}</div>`
90
- : html `<p class="meta contract-note">no scope was filed before planning — every term above is the planner's proposal</p>`;
91
+ ? html `<div class="contract-panel contract-drafted"><p class="approval-label">Your request</p><p class="meta">Nothing was filed before planning. The planner wrote this plan from the title and the project, so review every part as new.</p>${stale}</div>`
92
+ : html `<p class="meta contract-note">Nothing was filed before planning: every part above is the planner's.</p>`;
91
93
  }
92
94
  if (view.revision === true) {
93
95
  // A send-back: the terms were copied from the previous version, and the planner updated them with the notes.
94
96
  if (view.changes.length === 0) {
95
- return html `<div class="contract-panel contract-preserved"><p class="approval-label">updated plan</p><p><strong>Same terms as before</strong> <span class="meta">your notes fit the previous plan · <a href="/r/${view.run}">run ${view.run}</a></span></p>${stale}</div>`;
97
+ return html `<div class="contract-panel contract-preserved"><p class="approval-label">updated plan</p><p><strong>Same plan as before</strong> <span class="meta">your notes fit the previous plan · <a href="/r/${view.run}">Planner's record</a></span></p>${stale}</div>`;
96
98
  }
97
99
  return joinHtml([
98
100
  html `<div class="contract-panel contract-amended"${mode === "full" ? html ` id="contract-amendment"` : ""}><p class="approval-label">updated plan</p>`,
99
- html `<p><strong>${view.changes.length} change${view.changes.length === 1 ? "" : "s"} from your notes</strong> <span class="meta">— approving accepts the updated terms ${mode === "full" ? "in the scope" : "above"} · <a href="/r/${view.run}">run ${view.run}</a></span></p>`,
101
+ html `<p><strong>The planner changed ${view.changes.length === 1 ? "1 thing" : `${view.changes.length} things`} from your notes:</strong> <span class="meta">approving accepts the updated plan ${mode === "full" ? "shown in the plan" : "above"} · <a href="/r/${view.run}">Planner's record</a></span></p>`,
100
102
  view.amendment === null ? "" : html `<p class="recap contract-reason"><strong>Why:</strong> ${view.amendment}</p>`,
101
103
  html `<ul class="recap contract-changes">${view.changes.map(contractChangeHtml)}</ul>`,
102
104
  stale,
@@ -104,11 +106,11 @@ export function planContractHtml(view, mode) {
104
106
  ]);
105
107
  }
106
108
  if (view.changes.length === 0) {
107
- return html `<div class="contract-panel contract-preserved"><p class="approval-label">filed contract</p><p><strong>Preserved exactly</strong> <span class="meta">the plan reproduces the filed goal, exclusions, touches, and acceptance criteria — approving binds the terms you filed · <a href="/r/${view.run}">run ${view.run}</a></span></p>${stale}</div>`;
109
+ return html `<div class="contract-panel contract-preserved"><p class="approval-label">Your request</p><p><strong>Kept exactly</strong> <span class="meta">the plan keeps your goal, exclusions, files and requirements word for word · <a href="/r/${view.run}">Planner's record</a></span></p>${stale}</div>`;
108
110
  }
109
111
  return joinHtml([
110
- html `<div class="contract-panel contract-amended"${mode === "full" ? html ` id="contract-amendment"` : ""}><p class="approval-label">filed contract · amendment proposed</p>`,
111
- html `<p><strong>${view.changes.length} change${view.changes.length === 1 ? "" : "s"} to what you filed</strong> <span class="meta">— approving binds the AMENDED terms shown ${mode === "full" ? "in the scope" : "above"}, not the ones you filed · <a href="/r/${view.run}">run ${view.run}</a></span></p>`,
112
+ html `<div class="contract-panel contract-amended"${mode === "full" ? html ` id="contract-amendment"` : ""}><p class="approval-label">Your request · changes proposed</p>`,
113
+ html `<p><strong>The planner changed ${view.changes.length === 1 ? "1 thing" : `${view.changes.length} things`} from your request:</strong> <span class="meta">approving accepts the changed plan ${mode === "full" ? "shown in the plan" : "above"}, not your original words · <a href="/r/${view.run}">Planner's record</a></span></p>`,
112
114
  view.amendment === null
113
115
  ? html `<p class="meta">The planner stated no reason for the amendment</p>`
114
116
  : html `<p class="recap contract-reason"><strong>Why:</strong> ${view.amendment}</p>`,
@@ -165,7 +167,7 @@ export function revisionSentence(revision, acceptance) {
165
167
  return `Fixes what ${build} missed: ${missed.map(clean).join("; ")}.`;
166
168
  }
167
169
  const notes = revision.comments.map(one => clean(one.note)).filter(one => one !== "");
168
- return notes.length === 0 ? `Revises ${build}.` : `Fixes what ${build} missed: ${notes.join("; ")}.`;
170
+ return notes.length === 0 ? `Sends back ${build}.` : `Fixes what ${build} missed: ${notes.join("; ")}.`;
169
171
  }
170
172
  /** A planner's amendment, said plainly and kept in view: approving binds
171
173
  * the amended terms, so this is never folded away. */
@@ -242,11 +244,12 @@ export function approvalSheetHtml(input) {
242
244
  input.localOwner != null ? html `` : html `${reauth(html `<label class="approval-password"><span class="visually-hidden">Your password</span><input type="password" name="token" autocomplete="current-password" placeholder="Your password" aria-describedby="${parts.passwordNote}"></label>`)}`,
243
245
  input.localOwner?.eligible === true ? html ``
244
246
  : input.localOwner != null ? html `<p class="approval-password-note" id="${parts.passwordNote}">${input.localOwner.message} <a href="${input.localOwner.href}">${input.localOwner.action}</a></p>`
245
- : input.freshSince == null ? html `<p class="approval-password-note" id="${parts.passwordNote}">Your password signs this approval.</p>`
247
+ : input.freshSince == null ? html `<p class="approval-password-note" id="${parts.passwordNote}">Type your password to approve.</p>`
246
248
  : html `<p class="approval-password-note" id="${parts.passwordNote}" data-approval-fresh>Your password from ${whenTime(input.freshSince)} is remembered on this computer — leave this blank to approve with it, or type it again.</p>`,
247
249
  html `<button type="submit" data-primary-action>${parts.submit}</button></div>`,
248
250
  html `<p class="approval-after">${parts.after}</p>`,
249
- html `<div class="approval-secondary">${parts.editor ?? html `<a class="approval-link" href="${input.editHref}">Edit plan</a>`}<a class="approval-link" href="${input.notNowHref}">Not now</a></div>`,
251
+ // Approve, Request changes, Reject (docs/plans/plain-toolroll.md P1.3): Reject is the task's own Cancel, one tap away.
252
+ html `<div class="approval-secondary">${parts.editor ?? html `<a class="approval-link" href="${input.editHref}">Request changes</a>`}<a class="approval-link" href="${input.surface === "task" ? "#cancel-task" : input.notNowHref}">${input.surface === "task" ? "Reject" : "Not now"}</a></div>`,
250
253
  parts.details,
251
254
  ]), {
252
255
  attrs: { class: "approve-form approval-sheet", id: input.surface === "task" ? "approve" : null, "data-sticky": input.sticky },
@@ -272,7 +275,7 @@ export function approvalSheetParts(input) {
272
275
  const sentence = input.revision !== null && (repairText === null || input.revision.comments.length > 0) ? revisionSentence(input.revision, scope.acceptance)
273
276
  : repairText === null ? null
274
277
  : repairText[2] !== undefined ? `Fixes what ${builtBy} missed: ${statementsOf(repairText[2])}.`
275
- : repairText[3] !== undefined ? `Collects the evidence ${builtBy} was missing for: ${statementsOf(repairText[3])}. The saved code stays the same.`
278
+ : repairText[3] !== undefined ? `Shows what ${builtBy} couldn't show for: ${statementsOf(repairText[3])}. The saved code stays the same.`
276
279
  : `Fixes the checks that failed in ${builtBy}.`;
277
280
  const plan = input.planDocument === null ? null : parseExecutionPlanDocument(input.planDocument);
278
281
  const milestones = plan !== null && plan.ok ? plan.document.milestones : [];
@@ -326,7 +329,7 @@ export function approvalSheetParts(input) {
326
329
  : input.deliverable === "report"
327
330
  ? html `An agent investigates without changing the repository. You'll hear when its report is ready.`
328
331
  : html `An agent starts in its own branch. You'll hear when it's ready to review.`;
329
- const submit = rebuild !== null ? html `Approve & build again` : html `Approve & start`;
332
+ const submit = rebuild !== null ? html `Approve and retry` : html `Approve`;
330
333
  // Everything else the digest binds, one tap away.
331
334
  const detail = (title, body, attrs = html ``) => isEmptyHtml(body) ? html `` : html `<section class="approval-detail"${attrs}><h3>${title}</h3>${body}</section>`;
332
335
  const revisionDetail = input.revision === null ? "" : detail("Earlier build", joinHtml([
@@ -340,9 +343,9 @@ export function approvalSheetParts(input) {
340
343
  const amendment = approvalAmendmentHtml(contract);
341
344
  const contractDetail = input.planDocument === null || !isEmptyHtml(amendment) ? html ``
342
345
  : contract === null || contract === undefined ? html `<p class="meta">No record compares this plan with what was filed. Check the rows above against your request.</p>`
343
- : "problem" in contract ? html `<p class="meta">${contract.problem} · <a href="/r/${contract.run}">run ${contract.run}</a></p>`
346
+ : "problem" in contract ? html `<p class="meta">${contract.problem} · <a href="/r/${contract.run}">Planner's record</a></p>`
344
347
  : !contract.filed && contract.revision !== true ? html `<p class="meta">Nothing was filed before planning, so every term is the planner's proposal.</p>`
345
- : html `<p class="meta">${contract.revision === true ? "Same terms as the previous version." : "The plan keeps exactly what you filed."} <a href="/r/${contract.run}">Planning run ${contract.run}</a></p>`;
348
+ : html `<p class="meta">${contract.revision === true ? "Same terms as the previous version." : "The plan keeps exactly what you filed."} <a href="/r/${contract.run}">Planner's record</a></p>`;
346
349
  const criteriaDetail = scope.acceptance.length === 0 ? html ``
347
350
  : html `<ul class="approval-signed-criteria">${scope.acceptance.map(one => html `<li><code>${one.id}</code> ${one.statement} <span class="meta">shown by ${one.evidence.map(kind => EVIDENCE_PLAIN[kind] ?? kind).join(", ")}</span></li>`)}</ul>`;
348
351
  const projection = route?.projection ?? null;
@@ -364,13 +367,13 @@ export function approvalSheetParts(input) {
364
367
  const details = joinHtml([
365
368
  html `<details class="approval-details"><summary>Plan details</summary>`,
366
369
  revisionDetail,
367
- repairText === null ? "" : detail("Signed goal", html `<p>${scope.goal}</p>`),
370
+ repairText === null ? "" : detail("Full goal", html `<p>${scope.goal}</p>`),
368
371
  detail("Plan record", contractDetail),
369
- detail("Signed criteria", criteriaDetail),
372
+ detail("Requirements", criteriaDetail),
370
373
  detail("Why these agents", agentsDetail, html ` id="${input.surface === "task" ? "approval-agents" : "chat-approval-agents"}"`),
371
374
  detail("Runtime limits", limits),
372
375
  scope.candidate ? detail("Saved commit", html `<p><code class="approval-commit">${scope.candidate}</code></p>`) : "",
373
- detail("Seal", html `<p class="meta">Approval binds to this exact wording. <span class="seal mono">signs ${shortDigest(scope.digest)}</span></p>`),
376
+ detail("Plan version", html `<p class="meta">Approving agrees to exactly this plan. <span class="seal mono" title="${planVersionWords(scope.digest)}">${planVersionWords(scope.digest)}</span></p>`),
374
377
  html `</details>`,
375
378
  ]);
376
379
  const passwordNote = input.surface === "task" ? "approval-password-note" : "chat-approval-password-note";
@@ -384,21 +387,25 @@ export function approvalSheetParts(input) {
384
387
  const field = (name, value, rows, label, hint = "") => html `<label class="approval-field"><span class="approval-field-label">${label}</span>${hint === "" ? "" : html `<span class="approval-field-hint">${hint}</span>`}<textarea name="${name}" rows="${Math.min(10, Math.max(rows, value.split("\n").reduce((sum, line) => sum + Math.max(1, Math.ceil(line.length / 80)), 0)))}" form="${editForm}">${value}</textarea></label>`;
385
388
  // Done when, one line per requirement in the signed order; the server
386
389
  // matches them back by position (the digest guard keeps that order).
387
- const requirement = (name, value, label, placeholder = "") => html `<li><input type="text" name="${name}" value="${value}" aria-label="${label}"${placeholder === "" ? "" : html ` placeholder="${placeholder}"`} form="${editForm}"></li>`;
390
+ // Each requirement says how we'll know it is met (#19): a picker over the kinds a plan names, read back by position.
391
+ const howPicker = (name, chosen, label, planned) => html `<select name="${name}" aria-label="${label}" form="${editForm}" class="approval-how">${planned !== null && planned.length > 1 ? html `<option value="keep"${chosen === "keep" ? html ` selected` : ""}>${`As planned: ${planned.map(kind => EVIDENCE_PLAIN[kind] ?? kind).join(", ")}`}</option>` : ""}${HOW_WE_KNOW.map(kind => html `<option value="${kind}"${chosen === kind ? html ` selected` : ""}>${HOW_WE_KNOW_WORDS[kind]}</option>`)}</select>`;
392
+ const requirement = (name, value, label, how, placeholder = "") => html `<li><input type="text" name="${name}" value="${value}" aria-label="${label}"${placeholder === "" ? "" : html ` placeholder="${placeholder}"`} form="${editForm}">${how}</li>`;
388
393
  const requirements = draft?.getAll("requirement") ?? scope.acceptance.map(one => one.statement);
394
+ const hows = draft?.getAll("requirement-how") ?? scope.acceptance.map(one => one.evidence.length === 1 ? one.evidence[0] : "keep");
389
395
  const added = draft?.get("requirement-new") ?? "";
396
+ const addedHow = draft?.get("requirement-new-how") ?? "manual-review";
390
397
  const editor = edit === null ? null : joinHtml([
391
398
  html `<details class="approval-edit" id="plan-editor"${draft === null && !edit.open ? "" : html ` open`}>`,
392
- html `<summary class="approval-link"><span class="approval-edit-open">Edit plan</span><span class="approval-edit-close">Cancel</span></summary>`,
399
+ html `<summary class="approval-link"><span class="approval-edit-open">Request changes</span><span class="approval-edit-close">Cancel</span></summary>`,
393
400
  html `<div class="approval-editor">`,
394
401
  edit.problem === null ? "" : html `<p class="problem" role="alert">${edit.problem}</p>`,
395
402
  field("goal", draft?.get("goal") ?? goalShown, 3, "Goal"),
396
403
  field("touches", draft?.get("touches") ?? scope.touches.join("\n"), 2, "Changes", "Only in these paths, one per line. Leave empty for any file."),
397
404
  field("not", draft?.get("not") ?? scope.outOfScope ?? "", 2, "Won’t touch"),
398
405
  html `<fieldset class="approval-field"><legend class="approval-field-label">Done when</legend>`,
399
- html `<span class="approval-field-hint">Clear a line to drop it.</span><ul class="approval-requirements">`,
400
- requirements.map((one, index) => requirement("requirement", one, `Requirement ${index + 1}`)),
401
- requirement("requirement-new", added, "Add a requirement", "Add a requirement"),
406
+ html `<span class="approval-field-hint">Clear a line to drop it. Beside each, how we'll know it is met.</span><ul class="approval-requirements">`,
407
+ requirements.map((one, index) => requirement("requirement", one, `Requirement ${index + 1}`, howPicker("requirement-how", hows[index] ?? "manual-review", `How we'll know requirement ${index + 1} is met`, scope.acceptance[index]?.evidence ?? null))),
408
+ requirement("requirement-new", added, "Add a requirement", howPicker("requirement-new-how", addedHow, "How we'll know the new requirement is met", null), "Add a requirement"),
402
409
  html `</ul></fieldset>`,
403
410
  html `<div class="approval-edit-act"><button type="submit" form="${editForm}">Save plan</button>`,
404
411
  // A plan with written steps keeps its own editor for them.
@@ -442,7 +449,7 @@ export function approvalSheetParts(input) {
442
449
  routeEvidenceHtml(route, "approval-evidence"),
443
450
  consent,
444
451
  ]);
445
- return { lead, rows, editor, editorForm, details, submit, label: rebuild !== null ? "Approve and build again" : "Approve and start", after, allowing, passwordNote };
452
+ return { lead, rows, editor, editorForm, details, submit, label: rebuild !== null ? "Approve and retry" : "Approve", after, allowing, passwordNote };
446
453
  }
447
454
  /** Earlier versions of a task still in flight, in plain words: queued
448
455
  * while they only wait, running once one has started; null when none. */
@@ -457,6 +464,9 @@ export class PermissionsWouldChange extends Error {
457
464
  }
458
465
  /** Evidence kinds in plain words, for the signed criteria in Details. */
459
466
  export const EVIDENCE_PLAIN = { check: "the project check", screenshot: "screenshots", "changed-path": "changed files", "manual-review": "your own check" };
467
+ /** The kinds a requirement's "How we'll know" picker offers, in the order it lists them (#19). */
468
+ export const HOW_WE_KNOW = ["check", "screenshot", "changed-path", "manual-review"];
469
+ export const HOW_WE_KNOW_WORDS = { check: "The project check passes", screenshot: "A screenshot shows it", "changed-path": "The changed files show it", "manual-review": "You check it yourself" };
460
470
  /** The permission choice a sealed profile carries, as the scope form names it. */
461
471
  export function permissionModeOfProfile(profile) {
462
472
  if (profile === null || profile === undefined)
@@ -471,30 +481,36 @@ export function permissionModeOfProfile(profile) {
471
481
  /** Done when, from the in-place editor: each line keeps its requirement's
472
482
  * id by position, and its evidence and guidance while its words stand; a
473
483
  * rewritten one is yours to check, like an added one; an emptied one is
474
- * dropped. */
484
+ * dropped. A "How we'll know" choice beside a line (#19) names its one kind
485
+ * instead; "keep", or no choice at all (an older form), changes nothing. The
486
+ * saved plan is a new version either way: it needs approving like any edit. */
475
487
  export function requirementsFromEditor(body, current) {
476
488
  const used = new Set(current.map(one => one.id));
477
489
  let next = 1;
478
490
  while (used.has(`c${next}`))
479
491
  next++;
480
492
  const plain = (raw) => raw.replace(/\s+/g, " ").trim();
493
+ const hows = body.getAll("requirement-how");
494
+ const howOf = (raw) => raw !== undefined && HOW_WE_KNOW.includes(raw) ? raw : null;
481
495
  const kept = body.getAll("requirement").slice(0, current.length).flatMap((raw, index) => {
482
496
  const statement = plain(raw);
483
497
  const criterion = current[index];
498
+ const how = howOf(hows[index]);
484
499
  if (statement === "")
485
500
  return [];
501
+ const evidence = how === null ? null : [how];
486
502
  return statement === plain(criterion.statement)
487
- ? [{ id: criterion.id, statement: criterion.statement, evidence: [...criterion.evidence], how: criterion.how }]
488
- : [{ id: criterion.id, statement, evidence: ["manual-review"], how: null }];
503
+ ? [{ id: criterion.id, statement: criterion.statement, evidence: evidence ?? [...criterion.evidence], how: criterion.how }]
504
+ : [{ id: criterion.id, statement, evidence: evidence ?? ["manual-review"], how: null }];
489
505
  });
490
506
  const added = plain(body.get("requirement-new") ?? "");
491
- return added === "" ? kept : [...kept, { id: `c${next}`, statement: added, evidence: ["manual-review"], how: null }];
507
+ return added === "" ? kept : [...kept, { id: `c${next}`, statement: added, evidence: [howOf(body.get("requirement-new-how") ?? undefined) ?? "manual-review"], how: null }];
492
508
  }
493
509
  export function consentDoorOf(scope, route) {
494
510
  if (scope === null)
495
- return { open: false, title: "There is no scope to approve yet", why: "write the scope first", road: "scope" };
511
+ return { open: false, title: "There is no plan to approve yet", why: "write the plan first", road: "scope" };
496
512
  if (scope.profileState === "unresolved") {
497
- return { open: false, title: "The agent setup isn’t ready yet", why: scope.unresolvedReason ?? "the scope cannot say exactly what would run", road: "scope" };
513
+ return { open: false, title: "The agent setup isn’t ready yet", why: scope.unresolvedReason ?? "the plan can't say exactly what would run", road: "scope" };
498
514
  }
499
515
  // THE SAME STRICT PROJECTION THE SEAL USES (v48 integrity): before any
500
516
  // nonce, password field, or approve action renders, the stored scope
@@ -509,17 +525,17 @@ export function consentDoorOf(scope, route) {
509
525
  return { open: false, title: "The provider’s auth mode can’t be read", why: `${authority.problem} — restate it, then approve`, road: "agents" };
510
526
  }
511
527
  if (authority.reason === "unrouted") {
512
- return { open: false, title: "This scope predates agent routing", why: "its approval no longer stands, and an approval now must name exactly which agent plans, builds, and revises — re-file the scope (edit and save it) to route it under today’s agents, then approve it", road: "scope" };
528
+ return { open: false, title: "This plan was made before agents were chosen per task", why: "Toolroll is choosing its agents, then you'll approve them. An approval now names exactly which agent plans, builds and fixes it: edit and save the plan to choose today’s agents, then approve it", road: "scope" };
513
529
  }
514
- return { open: false, title: "The agents on file can’t be read", why: `${authority.problem} — re-file the scope (edit and save it) so it is routed again under today’s agents`, road: "scope" };
530
+ return { open: false, title: "The agents on file can’t be read", why: `${authority.problem}. Edit and save the plan to choose today’s agents again`, road: "scope" };
515
531
  }
516
532
  if (route === null || route === undefined)
517
533
  return { open: true };
518
534
  if (route.kind === "unreadable") {
519
- return { open: false, title: "The agents on file can’t be read", why: `${route.problem} — re-file the scope (edit and save it) so it is routed again under today’s agents`, road: "scope" };
535
+ return { open: false, title: "The agents on file can’t be read", why: `${route.problem}. Edit and save the plan to choose today’s agents again`, road: "scope" };
520
536
  }
521
537
  if (route.kind === "legacy") {
522
- return { open: false, title: "This scope predates agent routing", why: "its approval no longer stands, and an approval now must name exactly which agent plans, builds, and revises — re-file the scope (edit and save it) to route it under today’s agents, then approve it", road: "scope" };
538
+ return { open: false, title: "This plan was made before agents were chosen per task", why: "Toolroll is choosing its agents, then you'll approve them. An approval now names exactly which agent plans, builds and fixes it: edit and save the plan to choose today’s agents, then approve it", road: "scope" };
523
539
  }
524
540
  if (route.projection !== null && route.projection.problems.length > 0) {
525
541
  return { open: false, title: "The agents on file can’t run", why: `${route.projection.problems.join("; ")} — change the agents, then approve`, road: "agents" };
@@ -530,7 +546,7 @@ export function consentDoorOf(scope, route) {
530
546
  * the reason and the one act that opens it. */
531
547
  export function consentClosedHtml(taskId, door, surface) {
532
548
  const href = `${taskHref(taskId)}#${door.road === "agents" ? "agents" : "scope"}`;
533
- const act = door.road === "agents" ? "Change the agents →" : "Edit and re-file the scope →";
549
+ const act = door.road === "agents" ? "Change the agents →" : "Edit the plan →";
534
550
  if (surface === "chat") {
535
551
  return html `<section class="card chat-action-card consent-closed" id="task-chat-action"><span class="eyebrow">approval needs attention</span><h2>${door.title}</h2><p class="meta">${door.why}</p><a class="button-link" href="${href}">${act}</a></section>`;
536
552
  }
@@ -541,12 +557,12 @@ export function consentClosedHtml(taskId, door, surface) {
541
557
  }
542
558
  export function profileWords(scope) {
543
559
  if (scope.profileState === "unresolved") {
544
- return html `<p class="meta"><strong>Filed but unapprovable</strong> — ${scope.unresolvedReason ?? "the scope cannot say exactly what would run"}. Restate the scope to fix it.</p>`;
560
+ return html `<p class="meta"><strong>Filed, but it can't be approved yet</strong> — ${scope.unresolvedReason ?? "the plan can't say exactly what would run"}. Edit the plan to fix it.</p>`;
545
561
  }
546
562
  const profile = scope.profile ?? null;
547
563
  if (profile === null) {
548
564
  return (scope.digestVersion ?? 1) < 2
549
- ? html `<p class="meta">Approved before routing was bound — pinned at upgrade to the configuration of that day</p>`
565
+ ? html `<p class="meta">Approved before agents were chosen per task: it keeps the setup of that day</p>`
550
566
  : html ``;
551
567
  }
552
568
  const repair = profile.repairModel === "inherit" ? "same model" : profile.repairModel;
@@ -559,7 +575,7 @@ export function profileWords(scope) {
559
575
  : profile.provider === "gemini"
560
576
  ? html `<p class="meta">Runs on <span class="mono">gemini · ${profile.model}</span> — ${profile.approvalArgv === "yolo" ? "Full access via --approval-mode yolo; every tool auto-approved" : "Auto via --approval-mode auto_edit; edits auto-approved, other tools refused"}, no turn limit (${Math.round(profile.timeoutSeconds / 60)} min ${profile.timeoutKind === "idle" ? "without progress" : "per attempt"}), spend reported in tokens only; repairs on ${repair}, ${Math.round(profile.repairTimeoutSeconds / 60)} min</p>`
561
577
  : isAcpProfile(profile)
562
- ? html `<p class="meta">Runs on <span class="mono">${ACP_AGENTS[profile.provider].name} (via ACP) · ${profile.model}</span> — full access inside the OS fence; the agent runs any tool it chooses and nothing asks. No turn limit: stops after ${Math.round(profile.timeoutSeconds / 60)} min, or ${Math.round(profile.idleTimeoutSeconds / 60)} min silent; repairs on ${repair}, ${Math.round(profile.repairTimeoutSeconds / 60)} min, or ${Math.round(profile.repairIdleTimeoutSeconds / 60)} min silent.</p>`
578
+ ? html `<p class="meta">Runs on <span class="mono">${ACP_AGENTS[profile.provider].name} (via ACP) · ${profile.model}</span> — full access inside Toolroll's sandbox; the agent runs any tool it chooses and nothing asks. No turn limit: stops after ${Math.round(profile.timeoutSeconds / 60)} min, or ${Math.round(profile.idleTimeoutSeconds / 60)} min silent; repairs on ${repair}, ${Math.round(profile.repairTimeoutSeconds / 60)} min, or ${Math.round(profile.repairIdleTimeoutSeconds / 60)} min silent.</p>`
563
579
  : html `<p class="meta">Runs on <span class="mono">${profile.provider} · ${profile.model}</span> — ${profile.sandboxMode === "danger-full-access" ? "FULL permissions via --dangerously-bypass-approvals-and-sandbox; nothing asks" : "workspace-write sandbox"}, no turn limit (${Math.round(profile.timeoutSeconds / 60)} min ${profile.timeoutKind === "idle" ? "without progress" : "per attempt"}); repairs on ${repair}, ${Math.round(profile.repairTimeoutSeconds / 60)} min</p>`;
564
580
  // What the build may reach is part of what is approved (egress.ts): said on every approval form.
565
581
  // Its sandbox terms (project-sandbox.ts): what it reads always, and any widening, part of what the yes agrees to.
@@ -748,7 +764,7 @@ export function agentsCardHtml(taskId, view, canEdit) {
748
764
  routeEvidenceHtml(view),
749
765
  p === null
750
766
  ? view.kind === "unreadable"
751
- ? html `<p class="agents-halted">Nothing runs for this task until its scope is filed again and approved.</p>`
767
+ ? html `<p class="agents-halted">Nothing runs for this task until its plan is saved again and approved.</p>`
752
768
  : ""
753
769
  : joinHtml([
754
770
  p.halted ? html `<p class="agents-halted">Paused: a provider these agents need is reported unavailable. Nothing else is used in its place — choose another agent below, or restore the provider and report readiness again.</p>` : "",
@@ -794,7 +810,7 @@ export function approvalProofWords(proof) {
794
810
  if (proof.method === "chat")
795
811
  return " (from your paired chat)";
796
812
  if (proof.method === "carried")
797
- return " (carried from the release approval)";
813
+ return " (from the release approval)";
798
814
  // Easy-start C5: this computer's owner approving attended work in local mode, with no password.
799
815
  if (proof.method === "local-owner")
800
816
  return " (on this computer, no password)";
@@ -807,11 +823,11 @@ export function approveRefusalWords(reason) {
807
823
  if (reason === "machine-proof")
808
824
  return "not approved: only a person can approve this. Approve it on the task page or from your paired chat";
809
825
  if (reason === "archived")
810
- return "not approved: this task is put away. Restore it first";
826
+ return "not approved: this task is archived. Restore it first";
811
827
  if (reason === "unrouted")
812
- return "not approved: this scope predates agent routing and its old approval no longer stands — edit and re-file the scope so it is routed under today’s agents, then approve it";
828
+ return "not approved: this plan was made before agents were chosen per task, so its old approval no longer stands. Edit and save the plan to choose today’s agents, then approve it";
813
829
  if (reason === "profile-unresolved")
814
- return "not approved: the scope cannot name an exact agent for every role — fix the agent setup and re-file it";
830
+ return "not approved: the plan can't name an exact agent for every role. Fix the agent setup, then save the plan again";
815
831
  if (reason === "password-now")
816
832
  return "not approved: this needs your password typed now — protected work, raised permissions, or a plan an agent wrote can't use a remembered one";
817
833
  // A wrong password, someone who isn't an approver (or not for this project), a revoked login: one answer for all, so
@@ -941,7 +957,7 @@ export function taskControlHtml(control, taskId, csrf, surface, inert = false, s
941
957
  return html ``;
942
958
  const guarded = csrf !== "" && !inert;
943
959
  const runForm = (path, className, button) => postForm(`${taskHref(taskId)}/${path}`, button, { attrs: { class: className }, returnTo: surface, hidden: { run: control.run } });
944
- const role = (word) => word.role === "planner" ? "plan attempt" : word.role === "scout" ? "scouting attempt" : word.role === "reviewer" ? "review" : "build";
960
+ const role = (word) => word.role === "planner" ? "plan attempt" : word.role === "scout" ? "research attempt" : word.role === "reviewer" ? "review" : "build";
945
961
  if (control.kind === "stop" && surface === "chat") {
946
962
  return joinHtml([
947
963
  html `<section class="card task-control" id="task-control" data-task-control="stop" data-control-run="${control.run}"><details class="chat-stop-confirm"><summary>Stop task</summary>`,
@@ -953,7 +969,7 @@ export function taskControlHtml(control, taskId, csrf, surface, inert = false, s
953
969
  if (control.kind === "stop") {
954
970
  return joinHtml([
955
971
  html `<section class="card task-control" id="task-control" data-task-control="stop" data-control-run="${control.run}" aria-label="stop this attempt">`,
956
- html `<div class="task-control-copy"><details><summary>Stop details · ${role(control)} #${control.run}</summary><p class="meta">Stopping ends only this attempt's own processes. Its branch, uncommitted work, evidence, and decisions stay preserved; other tasks keep running.</p></details></div>`,
972
+ html `<div class="task-control-copy"><details><summary>Stop details · ${role(control)} #${control.run}</summary><p class="meta">Stopping ends only this attempt's own processes. Its branch, uncommitted work, saved files and decisions are kept; other tasks keep running.</p></details></div>`,
957
973
  stopElsewhere || !guarded || livePause === null || livePause.run !== control.run ? "" : pauseFormsHtml(taskId, livePause),
958
974
  stopElsewhere ? "" : guarded
959
975
  ? runForm("stop", "inline task-control-form task-stop-form", html `<button type="submit" class="danger task-control-button">Stop</button>`)
@@ -974,7 +990,7 @@ export function taskControlHtml(control, taskId, csrf, surface, inert = false, s
974
990
  const settled = control.stop.settledAt === null ? "" : ` · settled ${stopWhen(control.stop.settledAt)} (${control.stop.settlement ?? "?"})`;
975
991
  const kept = control.stop.settlement === "finished"
976
992
  ? html `The attempt reached its own ending (${control.outcome ?? "?"}) before the stop took effect; that outcome stands.`
977
- : html `${control.committed ? "Its commit is on the branch as a reviewable artifact; a" : "A"}ny uncommitted work is preserved${control.worktree === null ? "" : html ` in <span class="mono">${control.worktree}</span>`}. Resuming takes a fresh claim, re-proves the signed scope, and requires fresh proof — nothing is approved by resuming.`;
993
+ : html `${control.committed ? "Its commit is on the branch as a reviewable artifact; a" : "A"}ny uncommitted work is preserved${control.worktree === null ? "" : html ` in <span class="mono">${control.worktree}</span>`}. Resuming checks the approved plan again and needs new checks — resuming approves nothing.`;
978
994
  return joinHtml([
979
995
  html `<section class="card task-control" id="task-control" data-task-control="paused" data-control-run="${control.run}" aria-label="paused attempt">`,
980
996
  html `<div class="task-control-copy"><span class="eyebrow">paused</span><strong>${role(control)} #${control.run} was stopped by <span class="mono">${control.stop.requestedBy}</span></strong>`,
@@ -991,23 +1007,19 @@ export function taskControlHtml(control, taskId, csrf, surface, inert = false, s
991
1007
  * does and does not do, the gate that would still keep work from
992
1008
  * starting, and the password typed again. */
993
1009
  export function resumeCeremonyPage(chrome, data) {
994
- const back = html `<p class="meta"><a href="${data.returnTo === "chat" ? taskChatHref(data.taskId) : taskHref(data.taskId)}">Keep paused</a></p>`;
995
1010
  const control = data.control;
1011
+ // One card, one button (docs/plans/plain-toolroll.md P1.3): the same post, nonce, run and password rule as before.
1012
+ const blocked = !data.approved ? "The plan isn't approved as it stands, so nothing builds until it is approved again."
1013
+ : data.gate === null ? null : `${data.gate.summary}: ${data.gate.detail}`;
996
1014
  return screen("resume", joinHtml([
997
- html `<h1>Resume task?</h1><p class="resume-target"><strong>${data.taskTitle}</strong> · ${data.taskId} · Run #${control.run}</p>`,
998
- html `<div class="card resume-ceremony">`,
999
- html `<p class="row">Lifts this stop’s hold. Other holds stay in place.</p>`,
1000
- html `<p class="row">Continues saved work${control.committed ? " and its commit" : ""} under the current approved scope. The next attempt needs fresh evidence; the earlier handoff cannot count as a new result.</p>`,
1001
- html `<p class="row">Resuming grants no new approval or publishing permission. Scope, budget, agents, verification, and review limits still apply.</p>`,
1002
- html `<details><summary>Stop record and saved work</summary><p>Run #${control.run} was stopped by <span class="mono">${control.stop.requestedBy}</span> at ${stopWhen(control.stop.requestedAt)}${control.stop.settledAt === null ? "" : ` and settled ${stopWhen(control.stop.settledAt)} (${control.stop.settlement ?? "?"})`}.</p>${control.worktree === null ? "" : html `<p class="mono">${control.worktree}</p>`}</details>`,
1003
- data.approved ? "" : html `<p class="row problem-words"><strong>The scope is not approved as it stands</strong> — resuming lifts the pause, but no worker spends until the scope is approved again</p>`,
1004
- data.gate === null ? "" : html `<p class="row"><strong>Before work starts:</strong> ${data.gate.summary} — ${data.gate.detail}</p>`,
1005
- html `</div>`,
1015
+ html `<h1>Resume build #${control.run}?</h1>`,
1006
1016
  postForm(`${taskHref(data.taskId)}/resume`, joinHtml([
1007
- html `${reauth(html `<label>Confirm with your password<input type="password" name="token" autocomplete="current-password"></label>`)}`,
1008
- html `<button type="submit">Resume task</button>`,
1017
+ html `<p class="resume-target"><strong>${data.taskTitle}</strong></p>`,
1018
+ html `<p class="row">It continues from the saved work${control.committed ? " and its commit" : ""}. Resuming approves nothing new.</p>`,
1019
+ blocked === null ? "" : html `<p class="row problem-words">${blocked}</p>`,
1020
+ html `${reauth(html `<label>Your password<input type="password" name="token" autocomplete="current-password"></label>`)}`,
1021
+ html `<button type="submit">Resume</button>`,
1009
1022
  ], "\n"), { attrs: { class: "card resume-form" }, returnTo: data.returnTo, hidden: { nonce: data.nonceValue, run: control.run } }),
1010
- back,
1011
1023
  ], "\n"), { chrome });
1012
1024
  }
1013
1025
  export function taskViewSwitch(taskId, active) {
@@ -1041,7 +1053,7 @@ export function statusActionHref(status, taskId, runId, prUrl, repo) {
1041
1053
  /** A not-yet-finished task's shared headline from its dispatch diagnosis (task-status.ts). */
1042
1054
  export function dispatchHeadline(d) {
1043
1055
  const read = stageOfDispatch(d);
1044
- return taskStatusOf({ stage: read.stage, ...(read.need === undefined ? {} : { need: read.need }) }).headline;
1056
+ return taskStatusOf({ stage: read.stage, ...(read.need === undefined ? {} : { need: read.need }), ...(read.wait === undefined ? {} : { wait: read.wait }) }).headline;
1045
1057
  }
1046
1058
  export function statusLineHtml(status, extra = "") {
1047
1059
  return html `<span class="status-line" data-work-status="${status.token}" data-tone="${status.tone}"><i class="status-dot" aria-hidden="true"></i><span class="status-label">${status.label}</span>${extra}</span>`;
@@ -1169,7 +1181,7 @@ export function planRevisionLedgerHtml(ledger, taskId, csrf) {
1169
1181
  : joinHtml([
1170
1182
  html `<div class="plan-revision-pending"><span class="eyebrow">decision needed</span><h3>The agent recommends a plan change</h3>`,
1171
1183
  html `<p>${pendingRevision.reason}</p>`,
1172
- html `<p class="meta">Revision ${pendingRevision.revision}${pendingRevision.authorityKind === "authority-change" ? ` changes ${pendingRevision.changedFields.map(changedAuthority).join(" and ")} from what you approved, so work is paused until you decide.` : " only changes the route, not the approved outcome."}${pendingRevision.evidenceLink === null ? "" : ` Evidence: ${pendingRevision.evidenceLink}.`}</p>`,
1184
+ html `<p class="meta">Revision ${pendingRevision.revision}${pendingRevision.authorityKind === "authority-change" ? ` changes ${pendingRevision.changedFields.map(changedAuthority).join(" and ")} from what you approved, so work is paused until you decide.` : " only changes the route, not the approved outcome."}${pendingRevision.evidenceLink === null ? "" : ` Why: ${pendingRevision.evidenceLink}.`}</p>`,
1173
1185
  executionPlanHtml(pendingRevision.document, true),
1174
1186
  csrf === "" || pendingRevision.id === null
1175
1187
  ? ""
@@ -1211,7 +1223,7 @@ export function executionPlanHtml(document, compact = false) {
1211
1223
  html `<div class="execution-milestones"><span class="approval-label">milestones</span>${items(plan.milestones, true)}</div>`,
1212
1224
  html `<div class="execution-support"><div><span class="approval-label">dependencies</span>${items(plan.dependencies)}</div>`,
1213
1225
  html `<div><span class="approval-label">risks &amp; mitigations</span>${items(plan.risks)}</div></div>`,
1214
- html `<div class="execution-proof"><span class="approval-label">proof of done</span>${items(plan.proof)}</div>`,
1226
+ html `<div class="execution-proof"><span class="approval-label">how we'll know it's done</span>${items(plan.proof)}</div>`,
1215
1227
  html `</div>`,
1216
1228
  ]);
1217
1229
  }
@@ -1234,7 +1246,7 @@ export function revisionLineageWords(lineage) {
1234
1246
  const chain = lineage.ancestors.length > 1 ? ` · lineage ${lineage.ancestors.join(" → ")}` : "";
1235
1247
  words.push(`revises ${lineage.sourceTask}${lineage.sourceRun === null ? "" : ` (build #${lineage.sourceRun})`}${chain}`);
1236
1248
  if (!lineage.sourceHadScope) {
1237
- words.push("the source had no scope — nothing was inherited; the placeholder rubric waits for a real one");
1249
+ words.push("the earlier version had no plan, so nothing was copied; the placeholder requirements wait for real ones");
1238
1250
  }
1239
1251
  else if (lineage.terms !== null) {
1240
1252
  const terms = lineage.terms;
@@ -1318,7 +1330,7 @@ export function taskBodyParts(data) {
1318
1330
  const peek = html `<p class="meta">What is changing right now</p><div id="run-peek"><p class="meta">Watching… the first look lands within 15 seconds</p></div><p class="meta" id="run-peek-stamp"></p>`;
1319
1331
  const transcript = liveRun.provider !== "claude"
1320
1332
  ? html `<p class="meta">The live transcript needs the claude harness for now — this build runs on ${liveRun.provider}</p>`
1321
- : html `<p class="meta">What the agent is saying · display only — this is not evidence, and the machine running the agent could alter it</p><pre id="live-transcript" class="mono" style="max-height:18rem;overflow:auto;white-space:pre-wrap"></pre><p class="meta" id="live-transcript-state"></p>`;
1333
+ : html `<p class="meta">What the agent is saying</p><pre id="live-transcript" class="mono" style="max-height:18rem;overflow:auto;white-space:pre-wrap"></pre><p class="meta" id="live-transcript-state"></p>`;
1322
1334
  return html `<div class="card attempt-live" data-live-run="${liveRun.id}">${head}${peek}${transcript}${door}</div>`;
1323
1335
  })();
1324
1336
  // One truthful answer to the first question on a queued task: "will this
@@ -1338,9 +1350,9 @@ export function taskBodyParts(data) {
1338
1350
  if (task.state !== "done") {
1339
1351
  const diagnosis = data.dispatch ?? null;
1340
1352
  if (diagnosis === null)
1341
- return box("problem", "Dispatch unknown", "Refresh this task before relying on its scheduler state.", "unknown");
1353
+ return box("problem", "Can't tell yet", "Reload this task to see whether it can start.", "unknown");
1342
1354
  if (diagnosis.code === "running" && liveRun !== undefined) {
1343
- return box("ok", diagnosis.summary, html `Worker <span class="mono">${liveRun.runner}</span> owns <a href="/r/${liveRun.id}">build #${liveRun.id}</a>.`, diagnosis.code);
1355
+ return box("ok", diagnosis.summary, html `Builder <span class="mono">${liveRun.runner}</span> is on <a href="/r/${liveRun.id}">build #${liveRun.id}</a>.`, diagnosis.code);
1344
1356
  }
1345
1357
  const blocker = diagnosis.blockerTaskId === null
1346
1358
  ? null
@@ -1348,9 +1360,9 @@ export function taskBodyParts(data) {
1348
1360
  const action = (() => {
1349
1361
  switch (diagnosis.action) {
1350
1362
  case "start-worker": return "";
1351
- case "write-scope": return html ` <a href="#scope">Write the success contract</a> or use <strong>plan first</strong>.`;
1363
+ case "write-scope": return html ` <a href="#scope">Write the plan</a> or use <strong>plan first</strong>.`;
1352
1364
  case "select-agent": return html ` <a href="#scope">Choose an available provider and model</a>.`;
1353
- case "approve-scope": return html ` <a href="#approve">Review and sign the exact scope</a>.`;
1365
+ case "approve-scope": return html ` <a href="#approve">Review the plan and approve it</a>.`;
1354
1366
  case "answer-decision": return html ` <a href="#decisions">Answer the waiting question</a>.`;
1355
1367
  case "unhold": return html ` Use <strong>Remove hold</strong> when it can continue.`;
1356
1368
  // The reason is the detail itself; Retry is the status card's own act, said once.
@@ -1463,7 +1475,7 @@ export function taskBodyParts(data) {
1463
1475
  // c2" — never pretending the machine always disagreed.
1464
1476
  const machineNote = !reviewConflict(proof.proofMatrix, proof.machineVerdict, proof.proofVerdict)
1465
1477
  ? ""
1466
- : html `<p class="meta">An independent review found conflicting evidence.</p>`;
1478
+ : html `<p class="meta">An independent review found the report doesn't match the changes.</p>`;
1467
1479
  const chainHtml = repairChainHtml(proof.repairChain);
1468
1480
  // v51: semantic coverage sits beside the matrix, never inside the
1469
1481
  // verdict word — what the independent reviewer settled, under the
@@ -1480,7 +1492,7 @@ export function taskBodyParts(data) {
1480
1492
  // Damaged evidence (repair 2026-09-14): the box wears the receipt's
1481
1493
  // own problem words — never "ok" with a verdict that predates the damage.
1482
1494
  if (resultStatus.token === "evidence-damaged") {
1483
- return withReview(box("problem", resultStatus.label, html `<a href="/r/${proof.runId}">Build #${proof.runId}</a> finished. ${resultStatus.detail}${publicationWords}`, "evidence-damaged"), html `<div class="proof-review-actions"><a class="button-link" href="/r/${proof.runId}">Review the evidence problems</a></div>`, proofDetails);
1495
+ return withReview(box("problem", resultStatus.label, html `<a href="/r/${proof.runId}">Build #${proof.runId}</a> finished. ${resultStatus.detail}${publicationWords}`, "evidence-damaged"), html `<div class="proof-review-actions"><a class="button-link" href="/r/${proof.runId}">Review what doesn't match</a></div>`, proofDetails);
1484
1496
  }
1485
1497
  // A recorded pass the check read now doesn't stand behind (unreadable, failed since, or not read here) never
1486
1498
  // reads "passed": the box says what holds now (task-status.ts approvedCheckOf).
@@ -1501,7 +1513,7 @@ export function taskBodyParts(data) {
1501
1513
  const machineToken = proof.proofVerdict === "refuted" ? dispatchStatusToken("refuted") : dispatchStatusToken("short");
1502
1514
  return withReview(box(accepted ? "ok" : "problem", resultStatus.label, html `<a href="/r/${proof.runId}">Build #${proof.runId}</a> finished. ${verificationExplanation(proof.proofVerdict, proof.proofReasons)}${accepted ? html ` An approver accepted it by hand; that acceptance leaves the machine's verdict above unchanged.` : ""}${publicationWords}`, machineToken), html `<div class="proof-review-actions"><a class="button-link" href="${reviewHref(task.id)}">${accepted ? "Review the recorded exception" : resultStatus.action?.label ?? "Review evidence"}</a>${acceptForm}</div>`, proofDetails);
1503
1515
  }
1504
- return box("problem", "Dispatch unknown", "Refresh this task before relying on its scheduler state.", "unknown");
1516
+ return box("problem", "Can't tell yet", "Reload this task to see whether it can start.", "unknown");
1505
1517
  })();
1506
1518
  // Operator steering (arc 1, v120): notes for the agent, each wearing the
1507
1519
  // same words the CLI and the lead use — read (and when), waiting for the
@@ -1517,7 +1529,7 @@ export function taskBodyParts(data) {
1517
1529
  ? ""
1518
1530
  : joinHtml([
1519
1531
  postForm(`${taskHref(task.id)}/steer`, html `<input type="text" name="note" placeholder="guidance for the agent" aria-label="steering note" style="width:100%;max-width:28rem">${data.steeringRun == null ? "" : html `<input type="hidden" name="run" value="${String(data.steeringRun)}"><input type="hidden" name="key" value="${randomUUID()}"><input type="hidden" name="return" value="task">`}<button type="submit">${data.steeringRun == null ? "Save for the next attempt" : "Steer"}</button>`, { attrs: { class: "row" } }),
1520
- html `<p class="meta">${steerDestinationWords(steeringLive)} A note can't widen the approved scope.</p>`,
1532
+ html `<p class="meta">${steerDestinationWords(steeringLive)} A note can't widen the approved plan.</p>`,
1521
1533
  ]);
1522
1534
  const steeringCard = steerRows === "" && steerForm === "" ? "" : html `<h2>Steering</h2>${steerRows}${steerForm}`;
1523
1535
  const holds = data.holds.length === 0
@@ -1525,7 +1537,7 @@ export function taskBodyParts(data) {
1525
1537
  : joinHtml([
1526
1538
  html `<h2>Holds</h2>`,
1527
1539
  joinHtml(data.holds.map(hold => html `<p class="row">${holdOwnerWords(hold.ownerKind)} — ${hold.reason}${hold.until === null ? "" : html ` <span class="meta">until ${whenTime(hold.until)}</span>`}</p>`), "\n"),
1528
- html `<p class="meta">Only your hold can be lifted here — waits caused by questions, incidents, or retry delays clear on their own</p>`,
1540
+ html `<p class="meta">Only your hold can be lifted here. Waits for a question, repeated failures or a retry clear on their own.</p>`,
1529
1541
  ]);
1530
1542
  const approval = approvalOf(scope);
1531
1543
  // A refused save from the sheet's in-place editor reopens that editor with
@@ -1533,22 +1545,22 @@ export function taskBodyParts(data) {
1533
1545
  const inlineScopeDraft = data.scopeDraft?.has("requirement-new") === true;
1534
1546
  const scopeDraft = inlineScopeDraft ? undefined : data.scopeDraft;
1535
1547
  const scopeCard = scope === null
1536
- ? html `<p class="meta">No scope proposed — nothing builds this until one is approved</p>`
1548
+ ? html `<p class="meta">No plan yet. Nothing builds until one is approved.</p>`
1537
1549
  : joinHtml([
1538
1550
  html `<div class="card">`,
1539
1551
  html `<p><strong>Goal</strong></p><p class="recap">${scope.goal}</p>`,
1540
1552
  scope.outOfScope === null ? "" : html `<p><strong>Not this</strong></p><p class="recap">${scope.outOfScope}</p>`,
1541
- scope.touches.length === 0 ? "" : html `<p class="scope-paths"><strong>Touches</strong> ${scope.touches.join(", ")}</p>`,
1553
+ scope.touches.length === 0 ? "" : html `<p class="scope-paths"><strong>Files</strong> ${scope.touches.join(", ")}</p>`,
1542
1554
  html `<p><strong>Quality</strong> ${qualityModeTitle(scope.qualityMode ?? "default")}</p>`,
1543
1555
  acceptanceCeremonyHtml(scope.acceptance),
1544
1556
  scope.releaseTerms == null ? "" : html `<p><strong>Release</strong></p><p class="recap">${releaseTermsSentence(scope.releaseTerms)}</p>${data.releaseCiRunning === true ? html `<p class="meta">${RELEASE_CI_RUNNING_WORDS}</p>` : ""}${scope.releaseTerms.policy.carry ? html `<details><summary>What asks again</summary><ul>${joinHtml(releaseAsksAgainWords(scope.releaseTerms).map(line => html `<li>${line}</li>`), "")}</ul></details>` : ""}`,
1545
1557
  approval.approved && data.autoRelease != null
1546
- ? html `<p class="meta scope-seal">${autoReleaseWords(data.autoRelease)} · <span class="seal">signs ${shortDigest(scope.digest)}</span></p>`
1558
+ ? html `<p class="meta scope-seal">${autoReleaseWords(data.autoRelease)}</p>${planVersionDetailsHtml(scope.digest)}`
1547
1559
  : approval.approved && data.release?.kind === "carried"
1548
- ? html `<p class="meta scope-seal">Carried from ${data.release.approver ?? "a person"}'s release approval · <a href="${taskHref(data.release.anchorTask)}">Release ${data.release.version}</a> · <span class="seal">signs ${shortDigest(scope.digest)}</span></p>`
1560
+ ? html `<p class="meta scope-seal">Approved with ${data.release.approver ?? "a person"}'s release approval · <a href="${taskHref(data.release.anchorTask)}">Release ${data.release.version}</a></p>${planVersionDetailsHtml(scope.digest)}`
1549
1561
  : approval.approved
1550
- ? html `<p class="meta scope-seal">Approved by ${approval.by}${approvalProofWords(data.approvalProof)} · ${whenTime(approval.at)} · <span class="seal">signs ${shortDigest(scope.digest)}</span><span class="so-sr-only"> — approval binds to this exact wording</span></p>`
1551
- : html `<p class="meta"><span class="seal">signs ${shortDigest(scope.digest)}</span> — approval binds to this exact wording</p><p class="meta">Not approved${approval.reason === "changed" ? " — approved once, then rewritten" : ""}</p>`,
1562
+ ? html `<p class="meta scope-seal">Approved by ${approval.by}${approvalProofWords(data.approvalProof)} · ${whenTime(approval.at)}</p>${planVersionDetailsHtml(scope.digest)}`
1563
+ : html `<p class="meta">${approval.reason === "changed" ? "Edited after approval" : "Not approved"}</p>${planVersionDetailsHtml(scope.digest)}`,
1552
1564
  html `</div>`,
1553
1565
  ], "\n");
1554
1566
  // The scout's report (mate arc §10): title, summary, the document inert,
@@ -1557,20 +1569,20 @@ export function taskBodyParts(data) {
1557
1569
  // blank — the same rule as the revision brief.
1558
1570
  const reportCard = data.report === null || data.report === undefined
1559
1571
  ? data.deliverable === "report"
1560
- ? html `<div class="card"><p><strong>Scout task</strong> <span class="meta">delivers a report, never a branch — a read-only session investigates the goal and its report appears here when it finishes</span></p></div>`
1572
+ ? html `<div class="card"><p><strong>Research task</strong> <span class="meta">delivers a report, never a branch. A read-only session looks into the goal, and its report appears here when it finishes.</span></p></div>`
1561
1573
  : ""
1562
1574
  : !data.report.ok
1563
- ? html `<div class="card"><p><strong>The report</strong></p><p class="meta">${data.report.problem} · <a href="/r/${data.report.run}">run ${data.report.run}</a></p></div>`
1575
+ ? html `<div class="card"><p><strong>The report</strong></p><p class="meta">${data.report.problem} · <a href="/r/${data.report.run}">build #${data.report.run}</a></p></div>`
1564
1576
  : joinHtml([
1565
1577
  html `<div class="card report">`,
1566
- html `<p><strong>${data.report.report.title}</strong> <span class="meta">the scout's report · <a href="/r/${data.report.run}">run ${data.report.run}</a></span></p>`,
1578
+ html `<p><strong>${data.report.report.title}</strong> <span class="meta">the research report · <a href="/r/${data.report.run}">build #${data.report.run}</a></span></p>`,
1567
1579
  html `<p class="report-summary">${data.report.report.summary}</p>`,
1568
1580
  reportItemsHtml(data.report.report.items, data.report.shots, data.report.run),
1569
1581
  html `<pre class="recap plan-doc">${data.report.report.report}</pre>`,
1570
1582
  ...(data.report.report.followUps.length === 0
1571
1583
  ? []
1572
1584
  : [
1573
- html `<p><strong>Follow-ups the scout proposes</strong> <span class="meta">each files as a task in this repository; its scope still needs your approval</span></p>`,
1585
+ html `<p><strong>Follow-ups the research proposes</strong> <span class="meta">each files as a task in this project; its plan still needs your approval</span></p>`,
1574
1586
  ...data.report.report.followUps.map((one, index) => joinHtml([
1575
1587
  html `<div class="follow-up"><p><strong>${one.title}</strong></p><p class="meta">${one.goal}</p>`,
1576
1588
  data.csrf === "" || data.repo === null
@@ -1615,7 +1627,7 @@ export function taskBodyParts(data) {
1615
1627
  : joinHtml([
1616
1628
  approval.approved ? html `<details class="card planner-plan planner-plan-collapsed"><summary class="execution-plan-head">` : html `<section class="card planner-plan"><div class="execution-plan-head">`,
1617
1629
  html `<div><span class="eyebrow">execution plan</span><h2>${approval.approved ? `${planMilestoneCount ?? "Full"} step${planMilestoneCount === 1 ? "" : "s"} · open to review` : "How the agent will tackle this"}</h2>`,
1618
- approval.approved ? html `<p class="meta">The agent can adapt this route when evidence changes; your approved outcome stays fixed.</p>` : "",
1630
+ approval.approved ? html `<p class="meta">The agent can adapt this route as it learns more; your approved outcome stays fixed.</p>` : "",
1619
1631
  html `</div><span class="plan-lock">${planStanding}</span>${approval.approved ? html `</summary><div class="planner-plan-body">` : html `</div>`}`,
1620
1632
  executionPlanHtml(displayedPlanDocument ?? data.planDocument),
1621
1633
  approval.approved || sheetStatesContract ? "" : planContractHtml(data.planContract ?? null, "full"),
@@ -1683,7 +1695,7 @@ export function taskBodyParts(data) {
1683
1695
  deliverable: data.deliverable ?? "branch",
1684
1696
  rebuild: data.assignment?.need != null && "key" in data.assignment.need && data.assignment.need.key === "replan" ? data.assignment.need.build : null,
1685
1697
  editHref: data.csrf !== "" && data.planSha != null && data.planDocument !== null ? "#plan-edit" : "#scope",
1686
- notNowHref: "/work",
1698
+ notNowHref: "/tasks",
1687
1699
  sticky: approvalLeads,
1688
1700
  earlier: { active: data.assignment?.earlierActive ?? 0, running: data.assignment?.earlierRunning ?? 0 },
1689
1701
  edit: data.csrf === "" ? null
@@ -1693,21 +1705,21 @@ export function taskBodyParts(data) {
1693
1705
  const approveForm = scope === null || approval.approved || data.plan === "requested"
1694
1706
  ? ""
1695
1707
  : data.revision !== null && data.revision !== undefined && "problem" in data.revision
1696
- ? html `<div class="card approve-form" id="approve"><p><strong>This task is waiting on you: approval is blocked.</strong></p><p class="meta">${data.revision.problem} — a revision approves only against a brief that verifies</p></div>`
1708
+ ? html `<div class="card approve-form" id="approve"><p><strong>This task is waiting on you: approval is blocked.</strong></p><p class="meta">${data.revision.problem}. A revision can be approved only when its saved notes can be read.</p></div>`
1697
1709
  : scope.profileState === "unresolved"
1698
- ? html `<div class="card approve-form" id="approve"><p><strong>This task is waiting on you: its scope cannot be approved yet.</strong></p>${profileWords(scope)}<p class="ceremony-road"><a class="button-link" href="#scope">Edit the scope to fix it →</a></p></div>`
1710
+ ? html `<div class="card approve-form" id="approve"><p><strong>This task is waiting on you: its plan can't be approved yet.</strong></p>${profileWords(scope)}<p class="ceremony-road"><a class="button-link" href="#scope">Edit the plan to fix it →</a></p></div>`
1699
1711
  : !consentDoor.open
1700
1712
  ? consentClosedHtml(task.id, consentDoor, "task")
1701
1713
  : approvalSheetHtml(sheetInput);
1702
1714
  const scopeForm = joinHtml([
1703
- html `<details${scope === null || scopeDraft !== undefined ? html ` open` : ""}><summary>${scope === null ? "Write the scope" : "Edit the scope"}${approval.approved ? " (editing voids the approval)" : ""}</summary>`,
1715
+ html `<details${scope === null || scopeDraft !== undefined ? html ` open` : ""}><summary>${scope === null ? "Write the plan" : "Edit the plan"}${approval.approved ? " (it asks for approval again)" : ""}</summary>`,
1704
1716
  postForm(`${taskHref(task.id)}/scope`, joinHtml([
1705
1717
  "",
1706
1718
  scopeDraft === undefined || data.problem === null ? "" : html `<div class="problem" role="alert" id="scope-error">${data.problem}</div>`,
1707
1719
  html `<label>Goal<textarea name="goal" rows="3"${scopeDraft === undefined ? "" : html ` autofocus aria-describedby="scope-error"`}>${scopeDraft?.get("goal") ?? scope?.goal ?? ""}</textarea></label>`,
1708
1720
  html `<label>Not this<textarea name="not" rows="2">${scopeDraft?.get("not") ?? scope?.outOfScope ?? ""}</textarea></label>`,
1709
- html `<label>Touches <span class="meta">(one per line)</span><textarea name="touches" rows="2">${scopeDraft?.get("touches") ?? (scope?.touches ?? []).join("\n")}</textarea></label>`,
1710
- html `<label>Acceptance <span class="meta">(required — one criterion per line: <code>statement | evidence,kinds | how</code>; evidence kinds are check, screenshot, changed-path, manual-review; id is optional and auto-numbered)</span><textarea name="acceptance" rows="3" placeholder="The button opens the settings panel | screenshot">${scopeDraft?.get("acceptance") ?? acceptanceToLines(scope?.acceptance ?? []).join("\n")}</textarea></label>`,
1721
+ html `<label>Files <span class="meta">(one per line)</span><textarea name="touches" rows="2">${scopeDraft?.get("touches") ?? (scope?.touches ?? []).join("\n")}</textarea></label>`,
1722
+ html `<label>Done when <span class="meta">(required — one requirement per line: <code>what | how we'll know | note</code>; how we'll know is check, screenshot, changed-path or manual-review; ids number themselves)</span><textarea name="acceptance" rows="3" placeholder="The button opens the settings panel | screenshot">${scopeDraft?.get("acceptance") ?? acceptanceToLines(scope?.acceptance ?? []).join("\n")}</textarea></label>`,
1711
1723
  (() => {
1712
1724
  const defaults = data.spendDefaults ?? null;
1713
1725
  const budgetPrefill = scope?.budgetMicrousd != null
@@ -1726,13 +1738,13 @@ export function taskBodyParts(data) {
1726
1738
  ? scope.profile.sandboxMode === "danger-full-access" ? "bypassPermissions" : "auto"
1727
1739
  : null;
1728
1740
  const selected = scopeDraft?.get("permission-mode") === "bypassPermissions" ? "bypassPermissions" : scopeDraft?.get("permission-mode") === "auto" ? "auto" : data.permissionMode ?? profileMode ?? data.permissionDefault ?? "auto";
1729
- return html `<fieldset class="permission-field"><legend>Agent permissions</legend>${permissionModeChoices("permission-mode", selected)}<p class="meta permission-note">This task’s choice is sealed into its scope. Full access prevents permission prompts or sandbox limits from pausing supported unattended agents.</p></fieldset>`;
1741
+ return html `<fieldset class="permission-field"><legend>Agent permissions</legend>${permissionModeChoices("permission-mode", selected)}<p class="meta permission-note">This choice is part of the task's plan. Full access keeps permission prompts and sandbox limits from pausing agents that support it.</p></fieldset>`;
1730
1742
  })(),
1731
1743
  (() => {
1732
1744
  const selected = scopeDraft?.get("quality-mode") === "strict" ? "strict" : scopeDraft?.get("quality-mode") === "default" ? "default" : scope?.qualityMode ?? data.qualityMode ?? data.qualityDefault ?? "default";
1733
- return html `<fieldset class="permission-field"><legend>Quality</legend>${qualityModeChoices("quality-mode", selected)}<p class="meta permission-note">This choice is signed into the scope. Inspect the saved work and actual check results when it is ready. Publication and deployment need their own authorization.</p></fieldset>`;
1745
+ return html `<fieldset class="permission-field"><legend>Quality</legend>${qualityModeChoices("quality-mode", selected)}<p class="meta permission-note">This choice is part of the plan. Look at the saved work and the actual check results when it's ready. Publishing and deploying need their own approval.</p></fieldset>`;
1734
1746
  })(),
1735
- html `<button type="submit">Save scope</button>`,
1747
+ html `<button type="submit">Save plan</button>`,
1736
1748
  "",
1737
1749
  ], "\n"), { attrs: { class: "scope-editor" }, hidden: { sawDigest: scopeDraft?.get("sawDigest") ?? scope?.digest ?? "" } }),
1738
1750
  html `</details>`,
@@ -1835,46 +1847,46 @@ export function taskBodyParts(data) {
1835
1847
  // the scope's standing with its seal, publication, spend — the key
1836
1848
  // facts a reader scans before anything else, in one row grammar.
1837
1849
  const workerRow = liveRun !== undefined
1838
- ? prop("worker", html `${liveRun.runner} · <a href="/r/${liveRun.id}">build #${liveRun.id}</a> running`)
1850
+ ? prop("builder", html `${liveRun.runner} · <a href="/r/${liveRun.id}">build #${liveRun.id}</a> running`)
1839
1851
  : data.runs[0] !== undefined
1840
1852
  ? prop("last attempt", html `<a href="/r/${data.runs[0].id}">${runNoun(data.runs[0])} #${data.runs[0].id}</a> · ${data.runs[0].role === "planner" && data.runs[0].reason === "plan-drafted" ? "planned" : data.runs[0].reason === "interrupted" ? "interrupted" : data.runs[0].id === liveHistoryRunId ? "running" : data.runs[0].outcome ?? "never finished"} · ${data.runs[0].runner}`)
1841
1853
  : "";
1842
1854
  const queueRow = data.position !== null && data.position !== undefined && task.state === "queued"
1843
- ? prop("queue", html `${data.position.position} of ${data.position.total}${data.position.column === null ? " in the shared queue" : html ` in ${data.position.column}'s queue`} · <a href="/board?view=order">reorder</a>`)
1855
+ ? prop("queue", html `${data.position.position} of ${data.position.total}${data.position.column === null ? " in the shared queue" : html ` in ${data.position.column}'s queue`} · <a href="/tasks?view=order">reorder</a>`)
1844
1856
  : "";
1845
1857
  const scopeRow = scope === null
1846
- ? prop("scope", "none yet")
1858
+ ? prop("plan", "none yet")
1847
1859
  : approval.approved && data.autoRelease != null
1848
- ? prop("approved scope", html `<span class="seal">signs ${shortDigest(scope.digest)}</span> · ${autoReleaseWords(data.autoRelease)}`)
1860
+ ? prop("approved plan", html `${autoReleaseWords(data.autoRelease)}`)
1849
1861
  : approval.approved && data.release?.kind === "carried"
1850
- ? prop("approved scope", html `<span class="seal">signs ${shortDigest(scope.digest)}</span> · carried from ${data.release.approver ?? "a person"}'s release approval · <a href="${taskHref(data.release.anchorTask)}">Release ${data.release.version}</a>`)
1862
+ ? prop("approved plan", html `approved with ${data.release.approver ?? "a person"}'s release approval · <a href="${taskHref(data.release.anchorTask)}">Release ${data.release.version}</a>`)
1851
1863
  : approval.approved
1852
- ? prop("approved scope", html `<span class="seal">signs ${shortDigest(scope.digest)}</span> · ${qualityModeTitle(scope.qualityMode ?? "default")} · approved by ${approval.by}${approvalProofWords(data.approvalProof)} · ${whenTime(approval.at)}`)
1853
- : prop("scope", approval.reason === "changed" ? "rewritten since its approval — needs a new yes" : "not approved");
1854
- const strikesRow = data.strikes > 0 ? prop("strikes", `${data.strikes} failed attempt(s)`) : "";
1864
+ ? prop("approved plan", html `Approved by ${approval.by}${approvalProofWords(data.approvalProof)} · ${whenTime(approval.at)} · ${qualityModeTitle(scope.qualityMode ?? "default")}`)
1865
+ : prop("plan", approval.reason === "changed" ? "Edited after approval: it needs a new approval" : "not approved");
1866
+ const strikesRow = data.strikes > 0 ? prop("failed attempts", `${data.strikes} in a row`) : "";
1855
1867
  const propsCard = html `<div class="card props">${workerRow}${queueRow}${scopeRow}${publishesRow}${economics}${strikesRow}</div>`;
1856
1868
  // The same facts for the rebuilt page, row for row.
1857
1869
  const facts = [];
1858
1870
  const lastRun = data.runs[0];
1859
1871
  if (liveRun !== undefined)
1860
- facts.push({ label: "Worker", parts: [`${liveRun.runner} · `, { label: `build #${liveRun.id}`, href: `/r/${liveRun.id}` }, " running"] });
1872
+ facts.push({ label: "Builder", parts: [`${liveRun.runner} · `, { label: `build #${liveRun.id}`, href: `/r/${liveRun.id}` }, " running"] });
1861
1873
  else if (lastRun !== undefined)
1862
1874
  facts.push({ label: "Last attempt", parts: [{ label: `${runNoun(lastRun)} #${lastRun.id}`, href: `/r/${lastRun.id}` },
1863
1875
  ` · ${lastRun.role === "planner" && lastRun.reason === "plan-drafted" ? "planned" : lastRun.reason === "interrupted" ? "interrupted" : lastRun.id === liveHistoryRunId ? "running" : lastRun.outcome ?? "never finished"} · ${lastRun.runner}`] });
1864
1876
  if (data.position !== null && data.position !== undefined && task.state === "queued") {
1865
- facts.push({ label: "Queue", parts: [`${data.position.position} of ${data.position.total}${data.position.column === null ? " in the shared queue" : ` in ${data.position.column}'s queue`} · `, { label: "Reorder", href: "/board?view=order" }] });
1877
+ facts.push({ label: "Queue", parts: [`${data.position.position} of ${data.position.total}${data.position.column === null ? " in the shared queue" : ` in ${data.position.column}'s queue`} · `, { label: "Reorder", href: "/tasks?view=order" }] });
1866
1878
  }
1867
- facts.push(scope === null ? { label: "Scope", parts: ["none yet"] }
1868
- : approval.approved && data.autoRelease != null ? { label: "Approved scope", parts: [autoReleaseWords(data.autoRelease)] }
1869
- : approval.approved && data.release?.kind === "carried" ? { label: "Approved scope", parts: [`Carried from ${data.release.approver ?? "a person"}'s release approval · `, { label: `Release ${data.release.version}`, href: taskHref(data.release.anchorTask) }] }
1870
- : approval.approved ? { label: "Approved scope", parts: [{ seal: `signs ${scope.digest.length <= 12 ? scope.digest : `${scope.digest.slice(0, 12)}…`}` }, ` · ${qualityModeTitle(scope.qualityMode ?? "default")} · approved by ${approval.by}${approvalProofWords(data.approvalProof)}`, ...(approval.at === null ? [] : [" · ", { at: approval.at }])] }
1871
- : { label: "Scope", parts: [approval.reason === "changed" ? "rewritten since its approval — needs a new yes" : "not approved"] });
1879
+ facts.push(scope === null ? { label: "Plan", parts: ["none yet"] }
1880
+ : approval.approved && data.autoRelease != null ? { label: "Approved plan", parts: [autoReleaseWords(data.autoRelease)] }
1881
+ : approval.approved && data.release?.kind === "carried" ? { label: "Approved plan", parts: [`Approved with ${data.release.approver ?? "a person"}'s release approval · `, { label: `Release ${data.release.version}`, href: taskHref(data.release.anchorTask) }] }
1882
+ : approval.approved ? { label: "Approved plan", parts: [`Approved by ${approval.by}${approvalProofWords(data.approvalProof)}`, ...(approval.at === null ? [] : [" · ", { at: approval.at }]), ` · ${qualityModeTitle(scope.qualityMode ?? "default")}`] }
1883
+ : { label: "Plan", parts: [approval.reason === "changed" ? "Edited after approval: it needs a new approval" : "not approved"] });
1872
1884
  if (data.release?.kind === "anchor")
1873
1885
  facts.push({ label: "Release", parts: [`Approved for release ${data.release.version}`, ...(data.release.landed.length === 0 ? [] : [` · later commits: ${data.release.landed.map(one => one.sha.slice(0, 7)).join(", ")}`]), ...(data.release.carrying ? [] : [` · later commits ask again`])] });
1874
1886
  facts.push({ label: "Publishes as", parts: [publishesAs] });
1875
1887
  // v102: who asked for it, and what the project's approval rules need.
1876
1888
  if (data.filer != null)
1877
- facts.push({ label: "Filed by", parts: [data.filer.name === null ? "automation" : data.filer.kind === "coordinator" ? `${data.filer.name}, through a coordinator` : data.filer.name] });
1889
+ facts.push({ label: "Filed by", parts: [data.filer.name === null ? "automation" : data.filer.kind === "coordinator" ? `${data.filer.name}, through your lead` : data.filer.name] });
1878
1890
  if (data.approvalRules != null) {
1879
1891
  const votes = data.approvalRules.votes;
1880
1892
  facts.push({ label: "Approval rules", parts: [data.approvalRules.needsTwo && !approval.approved
@@ -1882,7 +1894,7 @@ export function taskBodyParts(data) {
1882
1894
  : data.approvalRules.words] });
1883
1895
  }
1884
1896
  // v103: everything an auditor asks about this task, printable or as JSON.
1885
- facts.push({ label: "Audit", parts: [{ label: "Evidence pack", href: `${taskHref(task.id)}/evidence` }] });
1897
+ facts.push({ label: "Audit", parts: [{ label: "Audit record", href: `${taskHref(task.id)}/evidence` }] });
1886
1898
  if (data.budgetHold != null)
1887
1899
  facts.push({ label: "Budget", parts: [`${data.budgetHold} · `, { label: "Spend", href: "/spend" }] });
1888
1900
  if (data.policyHold != null)
@@ -1967,7 +1979,7 @@ export function taskBodyParts(data) {
1967
1979
  : postForm(`${taskHref(task.id)}/block`, html `<select name="on" aria-label="task to wait for">${candidates.map(one => html `<option value="${one.id}">${one.id} — ${one.title}</option>`)}</select><button type="submit">Wait for this task</button><span class="meta"> — this task starts only after it finishes</span>`, { attrs: { class: "row" } });
1968
1980
  const waitsForCard = waitRows === "" && waitAdd === ""
1969
1981
  ? ""
1970
- : html `<h2>Waits for</h2>${waitRows === "" ? html `<p class="meta">Nothing — it starts when a worker is free</p>` : waitRows}${waitAdd}`;
1982
+ : html `<h2>Waits for</h2>${waitRows === "" ? html `<p class="meta">Nothing. It starts when a builder is free.</p>` : waitRows}${waitAdd}`;
1971
1983
  // The acts bar (task page pass): every verb in one row under the title,
1972
1984
  // the one that resolves this task's state first and primary — retry on a
1973
1985
  // stalled task, build-next in the queue, plan-first with no scope. The
@@ -1979,13 +1991,13 @@ export function taskBodyParts(data) {
1979
1991
  // A failed task's status card carries Retry itself (the same requeue); here it would be a second ink act.
1980
1992
  const cardRetries = task.state === "failed" && data.assignment != null && data.csrf !== "";
1981
1993
  const primaryAct = stopControlsActive || cardRetries ? null : stalled && !data.claimed
1982
- ? { html: act("requeue", "Retry, keeping the branch and workspace"), why: "Resolves the incidents, clears the failed attempts and queues the task again. The branch and workspace are kept.", whyClass: "retry" }
1994
+ ? { html: act("requeue", "Retry, keeping the branch and workspace"), why: "Clears the repeated failures and queues the task again. The branch and workspace are kept.", whyClass: "retry" }
1983
1995
  : canPlan
1984
- ? { html: act("plan", "plan first"), why: "plan first sends an agent to read the repository, ask you questions, and propose a scope — nothing builds until you approve it", whyClass: "plan" }
1996
+ ? { html: act("plan", "plan first"), why: "plan first sends an agent to read the project, ask you questions and propose a plan. Nothing builds until you approve it.", whyClass: "plan" }
1985
1997
  : data.plan === "requested"
1986
1998
  ? null
1987
1999
  : task.state === "queued" && !data.claimed && (data.position?.position ?? 1) > 1
1988
- ? { html: act("next", "build this next"), why: "moves it to the front of its queue — the next free worker looks here first; approval is still required", whyClass: "next" }
2000
+ ? { html: act("next", "build this next"), why: "moves it to the front of its queue: the next free builder takes it first. It still needs approval.", whyClass: "next" }
1989
2001
  : null;
1990
2002
  const holdAct = postForm(`${taskHref(task.id)}/hold`, html `<input type="text" name="reason" class="inline" placeholder="reason (optional)" aria-label="hold reason"><button type="submit">Hold the next attempt</button>`, { attrs: { class: "inline act-hold" } });
1991
2003
  const canHold = task.state === "queued" || task.state === "running" || task.state === "failed";
@@ -2039,7 +2051,7 @@ export function taskBodyParts(data) {
2039
2051
  ? ` · ${originWords(data.origin)}`
2040
2052
  : data.filedVia === null || data.filedVia === undefined
2041
2053
  ? ""
2042
- : ` · filed via ${data.filedVia}`}${data.deliverable === "report" ? html ` · <span class="badge">Scout</span>` : ""}</p>`;
2054
+ : ` · filed via ${data.filedVia}`}${data.deliverable === "report" ? html ` · <span class="badge">Research</span>` : ""}</p>`;
2043
2055
  // A pull request that can be merged (or opened) owns the one primary action; the result stays in its section.
2044
2056
  const pullRequestActs = data.csrf !== "" && (data.pullRequest?.view?.canMerge === true || (data.pullRequest?.view == null && data.pullRequest?.offer != null));
2045
2057
  const pullRequestFact = data.pullRequest?.fact != null && data.pullRequest.view?.runId === data.assignment?.receipt?.runId ? data.pullRequest.fact : undefined;
@@ -2083,7 +2095,7 @@ export function taskBodyParts(data) {
2083
2095
  return joinHtml([
2084
2096
  html `<div class="card"><p><strong>External work</strong> <span class="meta">${link} · ${state}</span></p>`,
2085
2097
  reopenable
2086
- ? postForm(`${taskHref(task.id)}/reopen`, html `${reauth(html `<input type="password" name="token" placeholder="your password" aria-label="your password" autocomplete="current-password">`)}<button type="submit">Reopen — the approved scope stands</button>`, { attrs: { class: "row" } })
2098
+ ? postForm(`${taskHref(task.id)}/reopen`, html `${reauth(html `<input type="password" name="token" placeholder="your password" aria-label="your password" autocomplete="current-password">`)}<button type="submit">Reopen — the approved plan stands</button>`, { attrs: { class: "row" } })
2087
2099
  : "",
2088
2100
  html `</div>`,
2089
2101
  ]);
@@ -2097,8 +2109,8 @@ export function taskBodyParts(data) {
2097
2109
  // The quarantine speaks here too (round-2 finding 5): the planner
2098
2110
  // is as fenced as the builder on a coordinator filing, so "plan
2099
2111
  // first" would recommend a road that refuses.
2100
- ? html `<div class="card"><p><strong>This task is waiting on you: an agent filed it, and it has no scope.</strong></p><p class="meta">Filed by <span class="mono">${data.coordinator.label}</span> — nothing plans, claims, or runs until you write a scope below and sign it. Your signature runs their request.</p></div>`
2101
- : html `<div class="card task-scope-needed"><p><strong>No approved scope yet</strong></p><p class="meta"><strong>Plan first</strong> drafts it from the repository, or <a href="#scope">write it yourself</a>.</p></div>`
2112
+ ? html `<div class="card"><p><strong>This task is waiting on you: an agent filed it, and it has no plan.</strong></p><p class="meta">Filed by <span class="mono">${data.coordinator.label}</span> — nothing plans or runs until you write a plan below and approve it. Your approval runs their request.</p></div>`
2113
+ : html `<div class="card task-scope-needed"><p><strong>No approved plan yet</strong></p><p class="meta"><strong>Plan first</strong> drafts it from the repository, or <a href="#scope">write it yourself</a>.</p></div>`
2102
2114
  : "";
2103
2115
  const approvalHtml = dependencyChoiceNeeded || approveForm === "" ? "" : approvalLeads
2104
2116
  ? html `<section class="task-plan-review" aria-label="Approve the plan">${approveForm}</section>`
@@ -2135,9 +2147,9 @@ export function taskBodyParts(data) {
2135
2147
  ? ""
2136
2148
  : section("proposals",
2137
2149
  // Confirm and dismiss come back to this task.
2138
- html `<h2>Proposed by coordinators</h2>${coordinatorProposalsSection(data.coordinatorProposals.rows, data.coordinatorProposals.decisions, data.coordinatorProposals.now, false, taskHref(data.task.id))}`, true, data.coordinatorProposals.rows.length),
2150
+ html `<h2>Proposed by your lead</h2>${coordinatorProposalsSection(data.coordinatorProposals.rows, data.coordinatorProposals.decisions, data.coordinatorProposals.now, false, taskHref(data.task.id))}`, true, data.coordinatorProposals.rows.length),
2139
2151
  section("decisions", decisions, true, data.decisions.length),
2140
- section("incidents", incidents, true, data.incidents.length),
2152
+ section("repeated failures", incidents, true, data.incidents.length, "incidents"),
2141
2153
  data.publication === null || data.publication === undefined
2142
2154
  ? ""
2143
2155
  : html `<p class="row"><span class="meta">published</span> ${safePrUrl(data.publication.prUrl) === null ? html `<span class="mono">PR #${data.publication.prNumber ?? "?"}</span>` : html `<a href="${safePrUrl(data.publication.prUrl)}" class="mono">PR #${data.publication.prNumber ?? "?"}</a>`} <span class="meta">${data.publication.state}${data.publication.remoteState !== null ? ` · ${data.publication.remoteState.toLowerCase()} on GitHub` : ""}${data.publication.lastCheckState !== null
@@ -2147,10 +2159,10 @@ export function taskBodyParts(data) {
2147
2159
  data.assignment == null ? section("attempts", runs, true, data.runs.length) : section("Build activity", data.runs.length === 0 ? "" : runRows, false, data.runs.length, "attempts"),
2148
2160
  section("usage", spendCard, false),
2149
2161
  section("steering", steeringCard, (data.steering ?? []).length > 0, (data.steering ?? []).length),
2150
- section("scope",
2162
+ section("plan",
2151
2163
  // The recipe road rides with the scope it reuses (UI polish
2152
2164
  // 2026-09-13), off the title-to-action path.
2153
- joinHtml([html `<h2>Scope</h2>`, scopeCard, releaseCardHtml(task.id, data.release ?? null, data.canStopRelease === true), data.repo !== null && data.scope !== null ? html `<p class="meta"><a href="/recipes/from-task?task=${encodeURIComponent(task.id)}">Reuse this scope as a recipe →</a></p>` : "", agentsCardHtml(task.id, data.route, data.canEditRoute === true), revisionInApproval ? "" : revisionCard, data.completion != null ? "" : repairChainHtml(data.repairChain ?? null), scopeForm], "\n"), scopeDraft !== undefined || (data.plan !== "requested" && approveForm === "" && !(scope === null && canPlan))),
2165
+ joinHtml([html `<h2>Plan</h2>`, scopeCard, releaseCardHtml(task.id, data.release ?? null, data.canStopRelease === true), html `<p class="meta">${data.repo !== null && data.scope !== null ? html `<a href="/recipes/from-task?task=${encodeURIComponent(task.id)}">Reuse this plan as a recipe →</a> · ` : ""}<a href="${taskHref(data.rootId ?? task.id)}/evidence">Audit record →</a></p>`, agentsCardHtml(task.id, data.route, data.canEditRoute === true), revisionInApproval ? "" : revisionCard, data.completion != null ? "" : repairChainHtml(data.repairChain ?? null), scopeForm], "\n"), scopeDraft !== undefined || (data.plan !== "requested" && approveForm === "" && !(scope === null && canPlan)), undefined, "scope"),
2154
2166
  dependencyChoiceNeeded ? "" : section("waits for", waitsForCard, (data.waitsFor ?? []).length > 0, (data.waitsFor ?? []).length),
2155
2167
  section("holds", holds, true, data.holds.length),
2156
2168
  cancelAct,
@@ -2263,7 +2275,7 @@ export function taskBodyParts(data) {
2263
2275
  : data.route?.legacy != null ? `${data.route.legacy.provider} · ${data.route.legacy.model}` : "The project's default"] });
2264
2276
  if (scope !== null)
2265
2277
  work.push({ label: "Checks level", parts: [qualityModeTitle(scope.qualityMode ?? "default")] });
2266
- for (const label of ["Worker", "Last attempt", "Queue", "Budget", "Policy", "Strikes"]) {
2278
+ for (const label of ["Builder", "Last attempt", "Queue", "Budget", "Policy", "Strikes"]) {
2267
2279
  const fact = facts.find(one => one.label === label);
2268
2280
  if (fact !== undefined)
2269
2281
  work.push(fact);
@@ -2286,9 +2298,9 @@ export function taskBodyParts(data) {
2286
2298
  links.push({ label: "Pull request", parts: [prHref === null ? `#${pr.prNumber ?? "?"}` : { label: `#${pr.prNumber ?? "?"}`, href: prHref }, ` · ${pr.label}`] });
2287
2299
  }
2288
2300
  const review = [];
2289
- const approvedScope = facts.find(one => one.label === "Approved scope" || one.label === "Scope");
2301
+ const approvedScope = facts.find(one => one.label === "Approved plan" || one.label === "Plan");
2290
2302
  if (approvedScope !== undefined)
2291
- review.push({ ...approvedScope, label: approvedScope.label === "Approved scope" ? "Approvals" : "Scope" });
2303
+ review.push({ ...approvedScope, label: approvedScope.label === "Approved plan" ? "Approvals" : "Plan" });
2292
2304
  review.push({ label: "Who reviews", parts: [facts.find(one => one.label === "Approval rules")?.parts[0] ?? "Any approver in this project"] });
2293
2305
  review.push({ label: "Publishes as", parts: [publishesAs] });
2294
2306
  if (data.resultRecord != null)
@@ -2367,10 +2379,8 @@ export function taskBodyParts(data) {
2367
2379
  ...(sectionIds.has("waits-for") ? [{ group: "work", label: "Dependencies", kind: "goto", tab: "plan", target: "waits-for" }] : []),
2368
2380
  ...(sectionIds.has("holds") ? [{ group: "work", label: "Holds", kind: "goto", tab: "plan", target: "holds" }] : []),
2369
2381
  ...(data.csrf !== "" ? [{ group: "work", label: "Task options", kind: "goto", tab: "plan", target: "task-diagnostics" }] : []),
2370
- ...(data.canPutAway === true && data.csrf !== "" && data.archived == null && !building ? [{ group: "work", label: "Put away", kind: "post", action: `${taskHref(task.id)}/archive`, fields: { return: taskHref(task.id) } }] : []),
2382
+ ...(data.canPutAway === true && data.csrf !== "" && data.archived == null && !building ? [{ group: "work", label: "Archive", kind: "post", action: `${taskHref(task.id)}/archive`, fields: { return: taskHref(task.id) } }] : []),
2371
2383
  { group: "share", label: "Copy link", kind: "copy" },
2372
- ...(facts.some(one => one.label === "Audit") ? [{ group: "share", label: "Evidence pack", kind: "link", href: `${taskHref(rootOf)}/evidence` }] : []),
2373
- ...(data.repo !== null && data.scope !== null ? [{ group: "share", label: "Reuse as recipe", kind: "link", href: `/recipes/from-task?task=${encodeURIComponent(task.id)}` }] : []),
2374
2384
  ...(cancelForm !== "" ? [{ group: "danger", label: "Cancel task", kind: "goto", tab: "plan", target: "cancel-task" }] : []),
2375
2385
  ];
2376
2386
  const view = {
@@ -2408,12 +2418,15 @@ export function taskBodyParts(data) {
2408
2418
  approve: data.archived != null || sheetInput === null || dependencyChoiceNeeded || data.csrf === "" ? null : approveKitOf(sheetInput, data.approvalFresh ?? null),
2409
2419
  approved: scope !== null && approval.approved ? approvedKitOf(scope, approval, data.approvalProof ?? null) : null,
2410
2420
  more,
2411
- confirmStopped: data.csrf !== "" && data.assignment?.primaryAction?.code === "confirm-stopped" && data.assignment.primaryAction.target.runId !== null
2412
- ? { action: `${taskHref(data.assignment.rootId)}/confirm-stopped`, run: data.assignment.primaryAction.target.runId, checked: needsCheck(data.assignment) } : null,
2421
+ audit: facts.some(one => one.label === "Audit") ? { label: "Full record", href: `${taskHref(rootOf)}/evidence` } : null,
2422
+ recheckStopped: data.csrf !== "" && data.assignment?.primaryAction?.code === "recheck-stopped" && data.assignment.primaryAction.target.runId !== null
2423
+ ? { action: `${taskHref(data.assignment.rootId)}/recheck-stopped`, run: data.assignment.primaryAction.target.runId } : null,
2413
2424
  rebuild,
2414
2425
  // The plan, progress and plan changes are thread entries now; the rest still needs a person here.
2415
2426
  lead: [
2416
2427
  { key: "history", html: data.history === undefined ? "" : htmlString(data.history) }, { key: "control", html: htmlString(stop === null ? controlHtml : taskControlDetailsHtml(data.control, task.id, data.csrf, "task", false, true)) }, { key: "problem", html: fragmentString(problemHtml) },
2428
+ { key: "route-change", html: data.routeChange == null ? "" : htmlString(html `<div class="card"><p>${data.routeChange}</p></div>`) },
2429
+ { key: "fast-grown", html: data.fastGrown == null || data.csrf === "" ? "" : htmlString(html `<div class="card"><p>${data.fastGrown}</p>${postForm(`${taskHref(task.id)}/keep-grown`, btn("Approve it", { variant: "primary", type: "submit" }))}</div>`) },
2417
2430
  { key: "pull-request", html: fragmentString(pullRequestCard) },
2418
2431
  { key: "needs-scope", html: fragmentString(needsScopeCard) },
2419
2432
  { key: "mirror", html: fragmentString(mirrorCard) }, { key: "attempt", html: fragmentString(attemptPanel) },
@@ -2541,7 +2554,7 @@ export function releaseCardHtml(taskId, release, canStop) {
2541
2554
  if (release === null)
2542
2555
  return html ``;
2543
2556
  if (release.kind === "carried")
2544
- return html `<div class="card" id="release"><p><strong>Carried from ${release.approver ?? "a person"}'s release approval</strong></p><p class="meta"><a href="${taskHref(release.anchorTask)}">Release ${release.version}</a> · pull request #${release.pr}</p></div>`;
2557
+ return html `<div class="card" id="release"><p><strong>Approved with ${release.approver ?? "a person"}'s release approval</strong></p><p class="meta"><a href="${taskHref(release.anchorTask)}">Release ${release.version}</a> · pull request #${release.pr}</p></div>`;
2545
2558
  const until = new Intl.DateTimeFormat("en-US", { weekday: "short", hour: "numeric", minute: "2-digit" }).format(new Date(release.expiresAt));
2546
2559
  const landed = release.landed.length === 0 ? html `<p class="meta">No later commits yet.</p>`
2547
2560
  : html `<ul>${joinHtml(release.landed.map(one => html `<li><a href="${taskHref(one.task)}">${one.sha.slice(0, 7)}</a> · ${one.files.map(file => `${file.path} (+${file.added} −${file.deleted})`).join(", ")}</li>`), "")}</ul>`;
@@ -2553,7 +2566,7 @@ export function releaseCardHtml(taskId, release, canStop) {
2553
2566
  release.obligations.length === 0 ? "" : html `<details><summary>Still to pass (${release.obligations.length})</summary><ul>${joinHtml(release.obligations.map(one => html `<li>${one}</li>`), "")}</ul></details>`,
2554
2567
  release.nightly === null ? "" : html `<p class="meta">Nightly check of main: ${release.nightly.outcome ?? "running"} at ${release.nightly.sha.slice(0, 7)}${release.openMainFailures > 0 ? ` · ${release.openMainFailures} open failure${release.openMainFailures === 1 ? "" : "s"}` : ""}</p>`,
2555
2568
  ...release.githubNightly.map(line => html `<p class="meta">${line}</p>`),
2556
- release.carrying && canStop ? postForm(`${taskHref(taskId)}/stop-carrying`, html `<button type="submit" class="secondary">Stop carrying later commits</button>`, { attrs: { class: "inline" } }) : "",
2569
+ release.carrying && canStop ? postForm(`${taskHref(taskId)}/stop-carrying`, html `<button type="submit" class="secondary">Ask again for later commits</button>`, { attrs: { class: "inline" } }) : "",
2557
2570
  html `</div>`,
2558
2571
  ], "\n");
2559
2572
  }
@@ -2571,7 +2584,7 @@ export function approveKitOf(input, freshSince) {
2571
2584
  fields: { nonce: input.nonce, digest: input.digest, ...(rebuild === null ? {} : { rebuild: String(rebuild) }) },
2572
2585
  label: parts.label,
2573
2586
  heading: rebuild !== null ? "The plan changed" : input.deliverable === "report" ? "Investigation ready" : "Plan ready",
2574
- text: `Approving signs plan version ${input.scope.digest.slice(0, 12)} and ${after}.${allowing}`,
2587
+ text: `Approving ${after}.${allowing}`,
2575
2588
  kind: "standard",
2576
2589
  facts: { passwordless: passwordlessRendering() || input.localOwner?.eligible === true, remembered: until !== null },
2577
2590
  rememberedUntil: until,
@@ -2586,7 +2599,11 @@ export function approvedKitOf(scope, approval, proof) {
2586
2599
  return null;
2587
2600
  const basis = scope.approvalBasis ?? null;
2588
2601
  const rule = basis === "mode" ? "your operating mode for this project" : basis === "grant" ? "its flow, which runs on its own" : basis === "schedule" ? "the schedule you approved"
2589
- : basis === "auto-green" || basis === "auto-check" ? "this project’s release approval (CI and the release check passed)" : basis === "carried" ? "the release approval it was carried under" : null;
2602
+ : basis === "auto-green" || basis === "auto-check" ? "this project’s release approval (CI and the release check passed)" : basis === "carried" ? "the release approval it came with" : null;
2590
2603
  const how = approvalProofWords(proof).replace(/^ \(|\)$/g, "");
2591
2604
  return { by: approval.by, at: approval.at, automatic: rule !== null, rule, how: how === "" ? (rule === null ? "password typed" : "") : how, version: scope.digest.slice(0, 12) };
2592
2605
  }
2606
+ /** The plan version, in Details: a short handle a person can compare, never on the approval line itself. */
2607
+ export function planVersionDetailsHtml(digest) {
2608
+ return html `<details class="scope-version"><summary class="meta">Details</summary><p class="meta"><span class="mono">${planVersionWords(digest)}</span></p></details>`;
2609
+ }