@mjasnikovs/pi-task 0.38.28 → 0.38.30

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 (370) hide show
  1. package/dist/config/config.d.ts +70 -70
  2. package/dist/config/config.js +26 -35
  3. package/dist/config/extension-list.d.ts +6 -5
  4. package/dist/config/extension-list.js +3 -2
  5. package/dist/config/reasoning-args.d.ts +9 -7
  6. package/dist/config/reasoning-args.js +12 -10
  7. package/dist/config/reasoning.d.ts +44 -105
  8. package/dist/config/reasoning.js +27 -704
  9. package/dist/config/register.d.ts +34 -48
  10. package/dist/config/register.js +41 -51
  11. package/dist/config/tool-list.d.ts +16 -16
  12. package/dist/config/tool-list.js +1 -1
  13. package/dist/remote/bridge.d.ts +19 -10
  14. package/dist/remote/bridge.js +3 -2
  15. package/dist/remote/broadcast.js +3 -1
  16. package/dist/remote/events.js +12 -11
  17. package/dist/remote/history.d.ts +1 -1
  18. package/dist/remote/protocol.d.ts +6 -3
  19. package/dist/remote/protocol.js +2 -1
  20. package/dist/remote/push.d.ts +16 -16
  21. package/dist/remote/push.js +27 -27
  22. package/dist/remote/register.d.ts +3 -3
  23. package/dist/remote/register.js +17 -19
  24. package/dist/remote/server.d.ts +9 -8
  25. package/dist/remote/server.js +15 -14
  26. package/dist/remote/session-state.d.ts +5 -4
  27. package/dist/remote/session-state.js +8 -5
  28. package/dist/remote/sw.d.ts +7 -6
  29. package/dist/remote/sw.js +7 -6
  30. package/dist/remote/tailscale.d.ts +4 -2
  31. package/dist/remote/tailscale.js +4 -2
  32. package/dist/remote/ui-highlight.js +6 -5
  33. package/dist/remote/ui-render.js +4 -4
  34. package/dist/remote/ui-script.js +24 -24
  35. package/dist/remote/ui-styles.d.ts +1 -1
  36. package/dist/remote/ui-styles.js +10 -13
  37. package/dist/remote/ui-tools.js +9 -6
  38. package/dist/shared/child-extensions.d.ts +29 -17
  39. package/dist/shared/child-extensions.js +29 -17
  40. package/dist/shared/child-output.d.ts +30 -24
  41. package/dist/shared/child-output.js +25 -17
  42. package/dist/shared/child-process.d.ts +47 -40
  43. package/dist/shared/child-process.js +50 -59
  44. package/dist/shared/command-watchdog.d.ts +22 -16
  45. package/dist/shared/command-watchdog.js +28 -21
  46. package/dist/shared/fs-text.d.ts +16 -10
  47. package/dist/shared/fs-text.js +16 -10
  48. package/dist/shared/git-runner.d.ts +25 -25
  49. package/dist/shared/git-runner.js +25 -25
  50. package/dist/shared/leaked-tool-call.d.ts +17 -11
  51. package/dist/shared/leaked-tool-call.js +23 -15
  52. package/dist/shared/model-endpoint.d.ts +29 -16
  53. package/dist/shared/model-endpoint.js +33 -21
  54. package/dist/shared/pi-invocation.d.ts +7 -4
  55. package/dist/shared/pi-invocation.js +12 -7
  56. package/dist/shared/pkg-version.d.ts +13 -5
  57. package/dist/shared/pkg-version.js +13 -5
  58. package/dist/shared/reasoning-capability.d.ts +35 -24
  59. package/dist/shared/reasoning-capability.js +35 -24
  60. package/dist/shared/stream-watchdog.d.ts +60 -44
  61. package/dist/shared/stream-watchdog.js +62 -45
  62. package/dist/task/accept-debt.d.ts +41 -43
  63. package/dist/task/accept-debt.js +73 -65
  64. package/dist/task/api-synthesis.d.ts +24 -21
  65. package/dist/task/api-synthesis.js +32 -26
  66. package/dist/task/apis-contract.d.ts +32 -64
  67. package/dist/task/apis-contract.js +32 -64
  68. package/dist/task/artifact-closure.d.ts +27 -13
  69. package/dist/task/artifact-closure.js +95 -67
  70. package/dist/task/auto-commit.d.ts +46 -35
  71. package/dist/task/auto-commit.js +51 -38
  72. package/dist/task/auto-io.d.ts +45 -25
  73. package/dist/task/auto-io.js +57 -29
  74. package/dist/task/auto-orchestrator.d.ts +26 -24
  75. package/dist/task/auto-orchestrator.js +178 -162
  76. package/dist/task/auto-prompts.d.ts +36 -24
  77. package/dist/task/auto-prompts.js +40 -26
  78. package/dist/task/autofix-ledger.d.ts +27 -25
  79. package/dist/task/autofix-ledger.js +29 -26
  80. package/dist/task/batch-test-task.d.ts +20 -12
  81. package/dist/task/batch-test-task.js +67 -60
  82. package/dist/task/boot-probe.d.ts +60 -44
  83. package/dist/task/boot-probe.js +91 -72
  84. package/dist/task/cancel-input.d.ts +30 -16
  85. package/dist/task/cancel-input.js +20 -11
  86. package/dist/task/cancel-points.d.ts +27 -20
  87. package/dist/task/cancel-points.js +30 -22
  88. package/dist/task/child-runner.d.ts +46 -51
  89. package/dist/task/child-runner.js +48 -49
  90. package/dist/task/child-status.d.ts +23 -16
  91. package/dist/task/child-status.js +23 -16
  92. package/dist/task/clamp-output.js +12 -5
  93. package/dist/task/command-run.d.ts +31 -28
  94. package/dist/task/command-run.js +44 -35
  95. package/dist/task/command-shrink.d.ts +25 -18
  96. package/dist/task/command-shrink.js +37 -31
  97. package/dist/task/command-watchdog.d.ts +9 -6
  98. package/dist/task/command-watchdog.js +21 -15
  99. package/dist/task/context-attribution.d.ts +34 -26
  100. package/dist/task/context-attribution.js +34 -26
  101. package/dist/task/context-silence.d.ts +39 -29
  102. package/dist/task/context-silence.js +35 -25
  103. package/dist/task/context-usage.d.ts +25 -7
  104. package/dist/task/context-usage.js +21 -6
  105. package/dist/task/contracts.d.ts +8 -4
  106. package/dist/task/contracts.js +25 -17
  107. package/dist/task/coverage-loop.d.ts +22 -18
  108. package/dist/task/coverage-loop.js +35 -30
  109. package/dist/task/critique-probes.d.ts +13 -14
  110. package/dist/task/critique-probes.js +50 -39
  111. package/dist/task/debug-log.d.ts +13 -5
  112. package/dist/task/debug-log.js +32 -20
  113. package/dist/task/decompose-fidelity.d.ts +11 -9
  114. package/dist/task/decompose-fidelity.js +38 -33
  115. package/dist/task/decompose-granularity.d.ts +41 -38
  116. package/dist/task/decompose-granularity.js +41 -38
  117. package/dist/task/deep-render-check.d.ts +22 -14
  118. package/dist/task/deep-render-check.js +40 -31
  119. package/dist/task/dropped-input.d.ts +12 -7
  120. package/dist/task/dropped-input.js +5 -2
  121. package/dist/task/enforce-attribution.d.ts +38 -47
  122. package/dist/task/enforce-attribution.js +46 -52
  123. package/dist/task/enforce-guidelines.d.ts +31 -20
  124. package/dist/task/enforce-guidelines.js +32 -21
  125. package/dist/task/enrichment.d.ts +7 -2
  126. package/dist/task/enrichment.js +26 -14
  127. package/dist/task/env-notes.d.ts +16 -7
  128. package/dist/task/env-notes.js +48 -31
  129. package/dist/task/env-template-closure.d.ts +4 -4
  130. package/dist/task/env-template-closure.js +42 -34
  131. package/dist/task/external-context.d.ts +28 -21
  132. package/dist/task/external-context.js +17 -12
  133. package/dist/task/failure-classifier.d.ts +4 -5
  134. package/dist/task/failure-classifier.js +6 -7
  135. package/dist/task/file-inventory.d.ts +15 -11
  136. package/dist/task/file-inventory.js +25 -22
  137. package/dist/task/final-gate-fix.d.ts +74 -86
  138. package/dist/task/final-gate-fix.js +97 -116
  139. package/dist/task/final-gate-progress.d.ts +29 -46
  140. package/dist/task/final-gate-progress.js +40 -51
  141. package/dist/task/final-gate.d.ts +64 -97
  142. package/dist/task/final-gate.js +192 -199
  143. package/dist/task/fix-child.d.ts +21 -27
  144. package/dist/task/fix-child.js +21 -27
  145. package/dist/task/foreign-path.d.ts +6 -5
  146. package/dist/task/foreign-path.js +0 -0
  147. package/dist/task/frozen-conflict.d.ts +9 -10
  148. package/dist/task/frozen-conflict.js +61 -64
  149. package/dist/task/frozen-path-guard.d.ts +35 -14
  150. package/dist/task/frozen-path-guard.js +56 -39
  151. package/dist/task/gate-child.d.ts +27 -28
  152. package/dist/task/gate-child.js +37 -35
  153. package/dist/task/gate-deps.d.ts +34 -27
  154. package/dist/task/gate-deps.js +169 -159
  155. package/dist/task/gate-tally.d.ts +77 -80
  156. package/dist/task/gate-tally.js +65 -68
  157. package/dist/task/git-state-guard.d.ts +15 -11
  158. package/dist/task/git-state-guard.js +76 -66
  159. package/dist/task/impl-widget.d.ts +25 -16
  160. package/dist/task/impl-widget.js +27 -17
  161. package/dist/task/implementation-thinking.d.ts +33 -31
  162. package/dist/task/implementation-thinking.js +5 -6
  163. package/dist/task/implementation-turn.d.ts +34 -31
  164. package/dist/task/implementation-turn.js +29 -27
  165. package/dist/task/inline-markdown.d.ts +20 -7
  166. package/dist/task/inline-markdown.js +15 -6
  167. package/dist/task/launch-config-gap.js +25 -39
  168. package/dist/task/launch-contract.d.ts +18 -21
  169. package/dist/task/launch-contract.js +28 -30
  170. package/dist/task/launch-manifest.d.ts +6 -2
  171. package/dist/task/launch-manifest.js +35 -34
  172. package/dist/task/ledger.js +16 -14
  173. package/dist/task/lint-fix.d.ts +6 -8
  174. package/dist/task/lint-fix.js +67 -69
  175. package/dist/task/loop-detector.d.ts +9 -8
  176. package/dist/task/loop-detector.js +16 -12
  177. package/dist/task/mid-run-input.d.ts +17 -15
  178. package/dist/task/mid-run-input.js +17 -15
  179. package/dist/task/orchestrator.d.ts +24 -28
  180. package/dist/task/orchestrator.js +62 -64
  181. package/dist/task/orientation.d.ts +18 -23
  182. package/dist/task/orientation.js +24 -31
  183. package/dist/task/owned-freeze-conflict.d.ts +21 -20
  184. package/dist/task/owned-freeze-conflict.js +52 -85
  185. package/dist/task/owned-freeze-reassign.d.ts +40 -60
  186. package/dist/task/owned-freeze-reassign.js +41 -61
  187. package/dist/task/parsers.d.ts +4 -2
  188. package/dist/task/parsers.js +4 -4
  189. package/dist/task/phases.d.ts +41 -48
  190. package/dist/task/phases.js +180 -248
  191. package/dist/task/plan-io.d.ts +6 -7
  192. package/dist/task/plan-io.js +6 -7
  193. package/dist/task/plan-orchestrator.d.ts +10 -8
  194. package/dist/task/plan-orchestrator.js +14 -10
  195. package/dist/task/plan-prompts.d.ts +6 -5
  196. package/dist/task/plan-prompts.js +6 -5
  197. package/dist/task/plan-readonly.d.ts +4 -5
  198. package/dist/task/plan-readonly.js +4 -5
  199. package/dist/task/plan-rounds.d.ts +17 -29
  200. package/dist/task/plan-rounds.js +21 -34
  201. package/dist/task/plan-session.d.ts +58 -72
  202. package/dist/task/plan-session.js +61 -83
  203. package/dist/task/probe-gaming.d.ts +28 -27
  204. package/dist/task/probe-gaming.js +0 -0
  205. package/dist/task/prohibition-probe.d.ts +14 -16
  206. package/dist/task/prompts.d.ts +3 -4
  207. package/dist/task/prompts.js +17 -26
  208. package/dist/task/qa-transcript.d.ts +15 -22
  209. package/dist/task/qa-transcript.js +15 -21
  210. package/dist/task/question-box.d.ts +17 -13
  211. package/dist/task/question-box.js +19 -15
  212. package/dist/task/question-dedup.d.ts +6 -7
  213. package/dist/task/question-dedup.js +13 -14
  214. package/dist/task/question-dialog.d.ts +22 -32
  215. package/dist/task/question-dialog.js +22 -32
  216. package/dist/task/question-source.d.ts +18 -44
  217. package/dist/task/question-source.js +22 -51
  218. package/dist/task/refuted-constraint.d.ts +11 -31
  219. package/dist/task/refuted-constraint.js +27 -51
  220. package/dist/task/regenerable-artifacts.d.ts +12 -31
  221. package/dist/task/regenerable-artifacts.js +12 -31
  222. package/dist/task/render-check.d.ts +11 -22
  223. package/dist/task/render-check.js +33 -46
  224. package/dist/task/repo-health-check.d.ts +10 -14
  225. package/dist/task/repo-health-check.js +17 -23
  226. package/dist/task/requirements.d.ts +38 -71
  227. package/dist/task/requirements.js +78 -126
  228. package/dist/task/research-fanout-budget.d.ts +51 -88
  229. package/dist/task/research-fanout-budget.js +51 -88
  230. package/dist/task/research-worker.d.ts +33 -36
  231. package/dist/task/research-worker.js +39 -61
  232. package/dist/task/resume-gap.d.ts +14 -15
  233. package/dist/task/root-cause-repair.d.ts +9 -9
  234. package/dist/task/root-cause-repair.js +28 -40
  235. package/dist/task/run-bracket.d.ts +10 -13
  236. package/dist/task/run-end.d.ts +12 -22
  237. package/dist/task/run-end.js +8 -16
  238. package/dist/task/run-final-gate.d.ts +19 -21
  239. package/dist/task/run-final-gate.js +62 -80
  240. package/dist/task/runner-globs.d.ts +12 -13
  241. package/dist/task/runner-globs.js +12 -13
  242. package/dist/task/runner-resolve.d.ts +9 -9
  243. package/dist/task/runner-resolve.js +22 -23
  244. package/dist/task/script-escape.d.ts +10 -12
  245. package/dist/task/script-escape.js +13 -14
  246. package/dist/task/serve-entry.d.ts +1 -1
  247. package/dist/task/serve-entry.js +22 -25
  248. package/dist/task/service-blocks.js +4 -2
  249. package/dist/task/shipped-source.d.ts +11 -29
  250. package/dist/task/shipped-source.js +11 -29
  251. package/dist/task/skip-escape.js +10 -14
  252. package/dist/task/spec-urls.d.ts +26 -65
  253. package/dist/task/spec-urls.js +26 -65
  254. package/dist/task/spec-validation.d.ts +17 -20
  255. package/dist/task/spec-validation.js +17 -20
  256. package/dist/task/stall-detector.d.ts +23 -30
  257. package/dist/task/stall-detector.js +23 -30
  258. package/dist/task/stream-watchdog.d.ts +14 -12
  259. package/dist/task/stream-watchdog.js +14 -12
  260. package/dist/task/substitution-probe.d.ts +17 -20
  261. package/dist/task/substitution-probe.js +17 -20
  262. package/dist/task/task-gates.d.ts +36 -41
  263. package/dist/task/task-gates.js +95 -106
  264. package/dist/task/task-io.d.ts +4 -4
  265. package/dist/task/task-io.js +4 -4
  266. package/dist/task/task-parsers.js +4 -3
  267. package/dist/task/task-provenance.d.ts +2 -2
  268. package/dist/task/task-provenance.js +11 -13
  269. package/dist/task/task-types.d.ts +4 -3
  270. package/dist/task/terminal-outcome.d.ts +14 -16
  271. package/dist/task/terminal-outcome.js +12 -14
  272. package/dist/task/test-assembly.d.ts +13 -20
  273. package/dist/task/test-assembly.js +13 -20
  274. package/dist/task/timings.d.ts +5 -3
  275. package/dist/task/timings.js +5 -3
  276. package/dist/task/title-label.d.ts +9 -4
  277. package/dist/task/title-label.js +9 -4
  278. package/dist/task/type-only-answer.d.ts +44 -52
  279. package/dist/task/type-only-answer.js +44 -52
  280. package/dist/task/unfailable-command.d.ts +18 -24
  281. package/dist/task/unfailable-command.js +21 -27
  282. package/dist/task/unknown-routing.d.ts +10 -4
  283. package/dist/task/unknown-routing.js +10 -4
  284. package/dist/task/user-directives.d.ts +5 -8
  285. package/dist/task/user-directives.js +5 -8
  286. package/dist/task/verify-quality.d.ts +18 -22
  287. package/dist/task/verify-quality.js +45 -46
  288. package/dist/task/verify-reconcile.d.ts +15 -10
  289. package/dist/task/verify-reconcile.js +45 -43
  290. package/dist/task/verify-resolution.d.ts +24 -20
  291. package/dist/task/verify-resolution.js +51 -50
  292. package/dist/task/verify-work.d.ts +59 -66
  293. package/dist/task/verify-work.js +101 -138
  294. package/dist/task/widget.d.ts +15 -14
  295. package/dist/task/widget.js +22 -17
  296. package/dist/task/wiring-claims.d.ts +25 -32
  297. package/dist/task/wiring-claims.js +30 -35
  298. package/dist/task/write-guard.d.ts +39 -39
  299. package/dist/task/write-guard.js +48 -51
  300. package/dist/task/yolo.d.ts +34 -30
  301. package/dist/task/yolo.js +42 -37
  302. package/dist/workers/abstention.d.ts +21 -41
  303. package/dist/workers/abstention.js +27 -48
  304. package/dist/workers/brave-search.d.ts +4 -3
  305. package/dist/workers/brave-search.js +5 -2
  306. package/dist/workers/brave-warning.d.ts +7 -4
  307. package/dist/workers/brave-warning.js +19 -7
  308. package/dist/workers/ddg-search.d.ts +6 -6
  309. package/dist/workers/ddg-search.js +18 -12
  310. package/dist/workers/docs-cache.js +5 -2
  311. package/dist/workers/docs-chunk.d.ts +30 -37
  312. package/dist/workers/docs-chunk.js +37 -41
  313. package/dist/workers/docs-core.d.ts +28 -44
  314. package/dist/workers/docs-core.js +25 -44
  315. package/dist/workers/docs-index.js +4 -3
  316. package/dist/workers/docs-lookup.d.ts +15 -22
  317. package/dist/workers/docs-lookup.js +12 -21
  318. package/dist/workers/docs-project.d.ts +15 -9
  319. package/dist/workers/docs-project.js +17 -10
  320. package/dist/workers/docs-resolve.d.ts +19 -20
  321. package/dist/workers/docs-resolve.js +35 -32
  322. package/dist/workers/docs-retrieve.d.ts +5 -6
  323. package/dist/workers/docs-retrieve.js +18 -15
  324. package/dist/workers/exa-search.d.ts +9 -6
  325. package/dist/workers/exa-search.js +23 -12
  326. package/dist/workers/fetch-core.d.ts +13 -16
  327. package/dist/workers/fetch-core.js +23 -23
  328. package/dist/workers/focused-extractor.d.ts +12 -12
  329. package/dist/workers/focused-extractor.js +16 -19
  330. package/dist/workers/html-clean.js +24 -14
  331. package/dist/workers/http-request.d.ts +28 -20
  332. package/dist/workers/http-request.js +22 -17
  333. package/dist/workers/npm-version.d.ts +28 -11
  334. package/dist/workers/npm-version.js +24 -15
  335. package/dist/workers/phantom-imports.d.ts +15 -12
  336. package/dist/workers/phantom-imports.js +30 -24
  337. package/dist/workers/pi-worker-core.d.ts +86 -54
  338. package/dist/workers/pi-worker-core.js +112 -112
  339. package/dist/workers/pi-worker-docs.d.ts +24 -19
  340. package/dist/workers/pi-worker-docs.js +67 -76
  341. package/dist/workers/pi-worker-fetch.d.ts +7 -3
  342. package/dist/workers/pi-worker-fetch.js +27 -19
  343. package/dist/workers/pi-worker-search.js +12 -8
  344. package/dist/workers/pi-worker.d.ts +9 -4
  345. package/dist/workers/pi-worker.js +23 -10
  346. package/dist/workers/reasoning-warning.d.ts +18 -17
  347. package/dist/workers/reasoning-warning.js +22 -20
  348. package/dist/workers/research-cache.js +50 -78
  349. package/dist/workers/search-core.js +7 -5
  350. package/dist/workers/search-types.d.ts +10 -9
  351. package/dist/workers/search-types.js +9 -8
  352. package/dist/workers/session-hint.d.ts +13 -14
  353. package/dist/workers/session-hint.js +8 -9
  354. package/dist/workers/shared.d.ts +21 -25
  355. package/dist/workers/shared.js +0 -0
  356. package/dist/workers/single-read-extension.d.ts +14 -7
  357. package/dist/workers/single-read-extension.js +14 -7
  358. package/dist/workers/single-read-guard.d.ts +25 -28
  359. package/dist/workers/single-read-guard.js +32 -32
  360. package/dist/workers/typeonly-log.d.ts +12 -9
  361. package/dist/workers/typeonly-log.js +29 -33
  362. package/dist/workers/worker-channels.d.ts +15 -23
  363. package/dist/workers/worker-channels.js +15 -23
  364. package/dist/workers/worker-failure.d.ts +38 -46
  365. package/dist/workers/worker-failure.js +31 -39
  366. package/dist/workers/worker-kill.d.ts +25 -26
  367. package/dist/workers/worker-kill.js +16 -19
  368. package/dist/workers/worker-profiles.d.ts +43 -53
  369. package/dist/workers/worker-profiles.js +30 -38
  370. package/package.json +10 -8
@@ -1,74 +1,48 @@
1
1
  /**
2
- * nexttask 5B — the two candidate bounds on worker:apis's project-source fan-out.
3
- *
4
- * ⚠ ONE of the levers in this file is wired: the RESCUE progress deadline
5
- * (`workerProgressCeilingMs`) SHIPPED ON in nexttask 9, on a PASS measured over 42
6
- * trials per arm against an instrument whose own false-break rate is on record at
7
- * 1.5%. CAP, SCALE and RESCUE-CARRY remain OFF unless their env var is set CAP
8
- * and SCALE were rejected on argument (see below), carry-forward was measured
9
- * HARMFUL on its own.
10
- *
11
- * The OFF levers exist so `scripts/live-research-fanout-budget-ab.ts` can run them
12
- * against the shipped baseline in the SAME build — the alternative (dist surgery)
13
- * measures a patched copy of the code and not the code. Nothing may read them
14
- * outside that harness until it reports PASS; a lever wired on argument rather
15
- * than measurement is the failure mode nexttasks exists to prevent.
16
- *
17
- * THE FAULT THEY TARGET (mx5 run 18, measured — scripts/research-restart-baserate.ts):
18
- * `worker:apis` fans out `pi-worker-docs(module: ".")` project-source lookups, each
19
- * of which spawns its own summarising child, and the per-worker wall-clock cap is
20
- * 240s. Pearson r(project lookups, worker wall clock) = 0.909 over 24 tasks. 0-4
21
- * lookups never timed out; every worker at >=46 lookups burned the FULL restart
22
- * budget — 3 attempts, 720s, two of them discarded whole. The 240s ceiling and a
23
- * 46-call fan-out are jointly unsatisfiable, so the timeout is not a backstop
24
- * there, it is the guaranteed outcome.
25
- *
26
- * TWO WAYS TO MAKE THEM SATISFIABLE, and the A/B — not this comment — decides:
2
+ * research-fanout-budget — the levers bounding worker:apis's project-source
3
+ * fan-out, and which of them is on.
4
+ *
5
+ * `workerProgressCeilingMs` is ON by default; its env var is the OFF switch. CAP
6
+ * (`projectDocsBudget`), SCALE (`fanoutTimeoutPolicy`) and RESCUE-CARRY
7
+ * (`workerCarryForward`) are OFF unless their env var is set. They stay in the
8
+ * shipped build so a harness can run them against the shipped baseline in the SAME
9
+ * build patching a copy of the code measures the copy, not the code. Nothing may
10
+ * read them outside such a harness.
11
+ *
12
+ * THE FAULT THEY TARGET. `worker:apis` fans out `pi-worker-docs(module: ".")`
13
+ * project-source lookups, and each one spawns its own summarising child. Under a
14
+ * fixed wall-clock cap, a large enough fan-out cannot finish inside it so the
15
+ * timeout is not a backstop there, it is the guaranteed outcome.
27
16
  *
28
17
  * CAP bound the fan-out to fit the ceiling. Told to the worker upfront
29
18
  * (projectDocsBudgetNotice) and enforced in the tool
30
- * (projectDocsBudgetExhausted), because run 18 shows the prompt alone
31
- * does not bind: the same worker is ALREADY told "be decisive" by
32
- * WORKER_TIMEOUT_HINT on every restart.
33
- * SCALE bound the ceiling to fit the fan-out: each project-source lookup
34
- * pushes the deadline out, up to a hard ceiling, so a worker that is
35
- * making progress is not killed for making progress.
36
- *
37
- * The risk each carries, and why the A/B's quality invariant is load-bearing: CAP
38
- * can produce a faster worker that ships a THINNER APIS section, which is a
39
- * regression wearing a win's clothes (memory/apis-contract-stage2-failed.md: a
40
- * lever moved behaviour 20/20 while fabricating 15% of it). SCALE can simply
41
- * spend the extra time and still time out, buying nothing.
42
- *
43
- * ─────────────────────────────────────────────────────────────────────────────
44
- * BOTH OF THE ABOVE ANSWER THE WRONG QUESTION. Kept for the record and for the
45
- * A/B's other arms, but they are not the fix.
46
- *
47
- * They argue about how long a worker may run. The actual defect is what happens
48
- * when it runs out: the attempt is killed and everything it produced is THROWN
49
- * AWAY, and the re-spawn is given a hint but no findings — so it re-reads the
50
- * same files against the same clock and dies in the same place. That is why
51
- * every worker at >=46 lookups burned the FULL budget rather than converging.
52
- * The r=0.909 correlation measures the amnesia, not an over-long task.
19
+ * (projectDocsBudgetExhausted). The prompt alone does not bind: the same
20
+ * worker is ALREADY told "be decisive" by WORKER_TIMEOUT_HINT
21
+ * (pi-worker-core.ts) on every restart.
22
+ * SCALE bound the ceiling to fit the fan-out: each project-source lookup pushes
23
+ * the deadline out, up to a hard ceiling, so a worker that is making
24
+ * progress is not killed for making progress.
25
+ *
26
+ * BOTH ANSWER THE WRONG QUESTION. They argue about how long a worker may run. The
27
+ * defect is what happens when it runs out: the attempt is killed, everything it
28
+ * produced is THROWN AWAY, and the re-spawn gets a hint but no findings — so it
29
+ * re-reads the same files against the same clock and dies in the same place.
53
30
  *
54
31
  * Judged against "the worker must return its work", CAP makes the worker read
55
- * LESS lowering the requirement so the metric goes green and SCALE is a
56
- * per-file constant that dies on one big file, and, being wall-clock, makes
57
- * answer quality a function of the user's hardware: the same task on a slower
58
- * local model loses its work and degrades. No constant fixes that.
59
- *
60
- * RESCUE (pi-worker-core.ts) carry the killed attempt's findings into the
61
- * next one and never return less than the best attempt produced, so a
62
- * restart CONVERGES instead of repeating; and deadline on lack of
63
- * PROGRESS rather than elapsed time, so "slow" and "stuck" stop being
64
- * the same verdict. Being stuck is already detected separately and
65
- * correctly by the output-stall probe (STALL_AFTER_MS), which resets
66
- * on progress and only kills when the model endpoint is unreachable.
67
- *
68
- * Its risk is its own, and the same quality invariant catches it: a half-written
69
- * entry replayed under "work already done" is exactly how a fabrication gets
70
- * laundered into a final answer. Hence the carry is framed as unverified, and
71
- * ungrounded-symbol and anti-synthesis counts gate the arm.
32
+ * LESS, lowering the requirement so the metric goes green; and SCALE is a per-file
33
+ * constant that dies on one big file and, being wall-clock, makes answer quality a
34
+ * function of the user's hardware the same task on a slower model loses its work.
35
+ *
36
+ * RESCUE (pi-worker-core.ts) carry the killed attempt's findings into the next
37
+ * one and never return less than the best attempt produced, so a restart
38
+ * CONVERGES instead of repeating; and deadline on lack of PROGRESS rather
39
+ * than elapsed time, so "slow" and "stuck" stop being the same verdict.
40
+ * Being stuck is already detected separately by the output-stall probe
41
+ * (STALL_AFTER_MS, worker-profiles.ts), which resets on progress.
42
+ *
43
+ * The carry has a risk of its own: a half-written entry replayed under "work
44
+ * already done" is how a fabrication gets laundered into a final answer. That is
45
+ * why the carry is framed to the worker as unverified.
72
46
  */
73
47
  /** Max project-source (`module: "."`) docs lookups per worker ATTEMPT. Unset = no cap. */
74
48
  export const PROJECT_DOCS_BUDGET_ENV = 'PI_TASK_PROJECT_DOCS_BUDGET';
@@ -99,12 +73,10 @@ export const RESEARCH_LEVER_ENVS = [
99
73
  * The levers, read ONCE, as a reader the profile table can be handed.
100
74
  *
101
75
  * WHY A SNAPSHOT AND NOT `process.env`. Every worker in one research phase must
102
- * see the same arm. The three lever values used to be resolved once in
103
- * `phases.ts` and threaded down as three separate `ResearchWorkerRun` fields for
104
- * exactly that reason; moving the resolution into the `research` profile would
105
- * have moved the READ down to each worker with it, and a harness that flips a
106
- * var mid-phase would then half-apply its own arm. Freezing the reader keeps the
107
- * read-once property while letting the profile own what the values MEAN.
76
+ * see the same lever values. A profile that read `process.env` itself would move
77
+ * the read down to each worker, and a var flipped mid-phase would then apply to
78
+ * some workers and not others. Freezing the reader keeps the read-once property
79
+ * while letting the profile own what the values MEAN.
108
80
  */
109
81
  export function snapshotLeverEnv(env = defaultEnv) {
110
82
  const snap = new Map(RESEARCH_LEVER_ENVS.map(k => [k, env(k)]));
@@ -149,14 +121,10 @@ export function workerCarryForward(env = defaultEnv) {
149
121
  /**
150
122
  * The absolute backstop for the progress-based deadline.
151
123
  *
152
- * WHY THIS NUMBER. It is not a budget and it does not decide how long a worker
153
- * may take — the no-progress deadline does that, and it resets on every tool call.
154
- * This is the last-resort bound on a worker that never stops moving (an infinite
155
- * tool-call loop the loop detector somehow misses), so its only requirement is to
156
- * sit clear of the real workload. Measured on 42 progress-arm trials
157
- * (`~/tmp/research-fanout-ab-v3`): median 275s, p90 523s, **max 730s**. 20 minutes
158
- * is 1.6x the observed worst case, and 1.7x the 720s the SHIPPED path already
159
- * spends on a worker that burns all three attempts and returns nothing.
124
+ * It is not a budget and it does not decide how long a worker may take — the
125
+ * no-progress deadline does that, and it resets on every tool call. This is the
126
+ * last-resort bound on a worker that never stops moving (a tool-call loop the loop
127
+ * detector misses), so its only requirement is to sit clear of the real workload.
160
128
  *
161
129
  * A ceiling that never fires in production is the correct behaviour for a
162
130
  * backstop, not evidence it is untested: it fires under test
@@ -168,12 +136,7 @@ export const DEFAULT_WORKER_PROGRESS_CEILING_MS = 1_200_000;
168
136
  /**
169
137
  * The progress-based deadline's ceiling, or null when the lever is OFF.
170
138
  *
171
- * SHIPPED ON as of nexttask 9 — the env var is now the OFF switch, not the on
172
- * switch. Measured baseline vs progress over 42 trials/arm on a calibrated
173
- * instrument (A/A false-break 1.5%): worker-timeout restarts 22/24 → 0/24,
174
- * degrades 8/24 → 0/24, entries up on all four high-fan-out fixtures (TASK_0021
175
- * 11.0 → 25.5), quality invariants HOLD, every treatment-arm ungrounded flag
176
- * hand-verified as an instrument artifact rather than a fabrication.
139
+ * ON by default, so the env var is the OFF switch, not the on switch:
177
140
  *
178
141
  * unset ON at DEFAULT_WORKER_PROGRESS_CEILING_MS
179
142
  * "0" | "off" OFF — the fixed elapsed-time cap, exactly as before
@@ -194,9 +157,9 @@ export function workerProgressCeilingMs(env = defaultEnv) {
194
157
  * The upfront half of the CAP arm, appended to the APIS worker's prompt.
195
158
  *
196
159
  * Upfront and NUMERIC on purpose. The worker cannot ration a budget it learns
197
- * about only when it is spent, and "be decisive" — which it already receives on
198
- * every timeout restart — is exactly the unquantified version that run 18 shows
199
- * it ignoring until the third attempt.
160
+ * about only when it is spent, and "be decisive" — WORKER_TIMEOUT_HINT, which it
161
+ * already receives on every timeout restart — is the unquantified version of the
162
+ * same ask.
200
163
  */
201
164
  export function projectDocsBudgetNotice(budget) {
202
165
  return (`\n\nLOOKUP BUDGET: you may make at most ${budget} project-source `
@@ -1,21 +1,17 @@
1
1
  /**
2
2
  * ONE research worker, cache-skip to persist.
3
3
  *
4
- * WHY IT IS A MODULE. This was a 228-line closure inside `phaseResearch` over
5
- * eleven locals, and inside it live the three RETRY GATES and their precedence:
6
- * the EMPTY-SECTION gate (the only one that can fail the phase), the
7
- * ZERO-RETRIEVAL gate and the SILENT gate (both of which discard a failed retry
8
- * and ship the original). Getting that order wrong is how a run either dies on a
9
- * legitimately empty section or ships one written from memory.
4
+ * WHY IT IS A MODULE. It holds the three RETRY GATES and their precedence: the
5
+ * EMPTY-SECTION gate (the only one that can fail the phase), the ZERO-RETRIEVAL
6
+ * gate and the SILENT gate (both of which discard a failed retry and ship the
7
+ * original). Getting that order wrong is how a run either dies on a legitimately
8
+ * empty section or ships one written from memory.
10
9
  *
11
- * The cost was in the TESTS. Reaching the gates meant a temp dir, a real task
12
- * file, and a fake spawn routed on prose lifted out of `prompts.ts` — plus, for
13
- * attempt-1-vs-attempt-2, a second sentence lifted out of a module-private
14
- * preamble constant. In a codebase whose whole workflow is re-wording prompts and
15
- * measuring what changed, that means a reworded preamble silently stops the gate
16
- * tests from testing the gate. Behind this interface a test scripts
17
- * `runWorker(label, attempt)` and states the `RunWorkerResult` fields a gate
18
- * reads.
10
+ * Behind `ResearchWorkerRun` a test scripts `runWorker(label, attempt)` and states
11
+ * the `RunWorkerResult` fields a gate reads. Reaching a gate through the phase
12
+ * instead would mean a temp dir, a real task file, and a fake spawn routed on
13
+ * prose lifted out of `prompts.ts` so re-wording a prompt would silently stop
14
+ * the gate tests from testing the gate.
19
15
  *
20
16
  * THE INVARIANT, stated once: the empty gate runs FIRST and is the only one that
21
17
  * can throw; the other two keep the original when their retry does not improve a
@@ -27,8 +23,8 @@ import type { SpawnFn } from '../shared/child-process.js';
27
23
  import type { DebugLine } from './debug-log.js';
28
24
  /**
29
25
  * One research worker's row. `section` is the heading its output is assembled
30
- * and cached under; `label` is its child NAME — what the loader, the debug trail
31
- * and the A/B ledgers print, and the key into `REASONING_GROUP_BY_CHILD`.
26
+ * and cached under; `label` is its child NAME — what the loader and the debug
27
+ * trail print, and the key into `REASONING_GROUP_BY_CHILD` (config/reasoning.ts).
32
28
  */
33
29
  export interface ResearchWorkerSpec {
34
30
  section: string;
@@ -52,7 +48,7 @@ export interface ResearchWorkerSpec {
52
48
  * loop-degrade banner or a hallucinated non-bullet fragment — is re-run ONCE
53
49
  * with this preamble. A legitimately-empty section is NOT retried. */
54
50
  retryIfSilent?: string;
55
- /** This worker can issue project-source docs lookups, so the 5B fan-out bounds
51
+ /** This worker can issue project-source docs lookups, so the fan-out bounds
56
52
  * apply to it (see task/research-fanout-budget.ts). */
57
53
  fanoutBounded?: true;
58
54
  }
@@ -65,6 +61,12 @@ export interface ResearchWorkerSpec {
65
61
  */
66
62
  export interface ResearchWorkerRun {
67
63
  runWorker: (label: string, input: RunWorkerInput) => Promise<RunWorkerResult>;
64
+ /**
65
+ * The parent session's context window, forwarded to every worker child.
66
+ * Nothing in pi's stream reports one, so a child that is not TOLD sits at 0 and
67
+ * the churn rule cannot fire — see `RunWorkerInput.contextWindow`.
68
+ */
69
+ contextWindow: number | 'unknown';
68
70
  cwd: string;
69
71
  taskId: string;
70
72
  signal: AbortSignal;
@@ -80,22 +82,20 @@ export interface ResearchWorkerRun {
80
82
  /**
81
83
  * Read one worker's cached output back, or '' when there is none.
82
84
  *
83
- * A SEAM, and the symmetric half of `persistSection`. The cache skip is one
84
- * of the four outcomes this driver has, and reaching it used to require a
85
- * real task file on disk — which is most of why the gate tests needed a temp
86
- * dir at all.
85
+ * A SEAM, and the symmetric half of `persistSection`. The cache skip is one of
86
+ * the four outcomes this driver has, and reaching it any other way would need a
87
+ * real task file on disk.
87
88
  */
88
89
  readCached: (heading: string) => Promise<string>;
89
90
  /** Write one validated section to the task file. Serialised by the caller. */
90
91
  persistSection: (heading: string, text: string) => Promise<void>;
91
92
  /**
92
- * The 5B lever env vars, READ ONCE for the whole phase.
93
+ * The fan-out lever env vars, READ ONCE for the whole phase.
93
94
  *
94
- * Was three resolved values (`carryForward`, `fanoutTimeout`,
95
- * `progressCeilingMs`). It is one frozen reader now because the `research`
96
- * profile owns what those values mean; what this layer still owns is that
97
- * every worker in a run sees the SAME arm, which a live `process.env` read
98
- * per worker would lose. See `snapshotLeverEnv`.
95
+ * A frozen reader rather than resolved values, because the `research` profile
96
+ * (workers/worker-profiles.ts) owns what those values MEAN. What this layer
97
+ * owns is that every worker in one run sees the same values, which a live
98
+ * `process.env` read per worker would lose. See `snapshotLeverEnv`.
99
99
  */
100
100
  leverEnv: (key: string) => string | undefined;
101
101
  }
@@ -130,15 +130,12 @@ export declare function researchWorkerCacheHeading(section: string): string;
130
130
  * wrote nothing): NOT a failure. On an extremely simple task ("create a folder
131
131
  * with an index.html in it") three of the four workers have genuinely nothing
132
132
  * to report, and each worker prompt tells the model to emit ONLY what this task
133
- * touches and to drop everything else — so silence is the CORRECT answer and
134
- * was killing the whole task at research (issue #10). Measured live on the
135
- * issue's own prompt (30 reps/worker, local Qwen3.6-27B): every APIS answer was
136
- * semantically "there is nothing here", and 2/30 were literally zero bytes on a
137
- * clean exit the other 28 survived only because the model happened to wrap the
138
- * same non-answer in a parenthetical, which is model style, not signal. The
139
- * caller retries once and then accepts an explicit empty section; what stays
140
- * fatal is silence WITH a reported cause, which is the masked-disconnect case
141
- * this branch was written for and which `modelError` now names outright.
133
+ * touches and to drop everything else — so silence is the CORRECT answer, and
134
+ * treating it as a failure kills the whole task at research. "Nothing here"
135
+ * and zero bytes are the same answer; which one a model writes is style, not
136
+ * signal. The caller retries once and then accepts an explicit empty section.
137
+ * What stays fatal is silence WITH a reported cause, the masked-disconnect
138
+ * case `modelError` names outright.
142
139
  *
143
140
  * Returns null when the result is trustworthy.
144
141
  */
@@ -1,21 +1,17 @@
1
1
  /**
2
2
  * ONE research worker, cache-skip to persist.
3
3
  *
4
- * WHY IT IS A MODULE. This was a 228-line closure inside `phaseResearch` over
5
- * eleven locals, and inside it live the three RETRY GATES and their precedence:
6
- * the EMPTY-SECTION gate (the only one that can fail the phase), the
7
- * ZERO-RETRIEVAL gate and the SILENT gate (both of which discard a failed retry
8
- * and ship the original). Getting that order wrong is how a run either dies on a
9
- * legitimately empty section or ships one written from memory.
4
+ * WHY IT IS A MODULE. It holds the three RETRY GATES and their precedence: the
5
+ * EMPTY-SECTION gate (the only one that can fail the phase), the ZERO-RETRIEVAL
6
+ * gate and the SILENT gate (both of which discard a failed retry and ship the
7
+ * original). Getting that order wrong is how a run either dies on a legitimately
8
+ * empty section or ships one written from memory.
10
9
  *
11
- * The cost was in the TESTS. Reaching the gates meant a temp dir, a real task
12
- * file, and a fake spawn routed on prose lifted out of `prompts.ts` — plus, for
13
- * attempt-1-vs-attempt-2, a second sentence lifted out of a module-private
14
- * preamble constant. In a codebase whose whole workflow is re-wording prompts and
15
- * measuring what changed, that means a reworded preamble silently stops the gate
16
- * tests from testing the gate. Behind this interface a test scripts
17
- * `runWorker(label, attempt)` and states the `RunWorkerResult` fields a gate
18
- * reads.
10
+ * Behind `ResearchWorkerRun` a test scripts `runWorker(label, attempt)` and states
11
+ * the `RunWorkerResult` fields a gate reads. Reaching a gate through the phase
12
+ * instead would mean a temp dir, a real task file, and a fake spawn routed on
13
+ * prose lifted out of `prompts.ts` so re-wording a prompt would silently stop
14
+ * the gate tests from testing the gate.
19
15
  *
20
16
  * THE INVARIANT, stated once: the empty gate runs FIRST and is the only one that
21
17
  * can throw; the other two keep the original when their retry does not improve a
@@ -57,15 +53,12 @@ export function researchWorkerCacheHeading(section) {
57
53
  * wrote nothing): NOT a failure. On an extremely simple task ("create a folder
58
54
  * with an index.html in it") three of the four workers have genuinely nothing
59
55
  * to report, and each worker prompt tells the model to emit ONLY what this task
60
- * touches and to drop everything else — so silence is the CORRECT answer and
61
- * was killing the whole task at research (issue #10). Measured live on the
62
- * issue's own prompt (30 reps/worker, local Qwen3.6-27B): every APIS answer was
63
- * semantically "there is nothing here", and 2/30 were literally zero bytes on a
64
- * clean exit the other 28 survived only because the model happened to wrap the
65
- * same non-answer in a parenthetical, which is model style, not signal. The
66
- * caller retries once and then accepts an explicit empty section; what stays
67
- * fatal is silence WITH a reported cause, which is the masked-disconnect case
68
- * this branch was written for and which `modelError` now names outright.
56
+ * touches and to drop everything else — so silence is the CORRECT answer, and
57
+ * treating it as a failure kills the whole task at research. "Nothing here"
58
+ * and zero bytes are the same answer; which one a model writes is style, not
59
+ * signal. The caller retries once and then accepts an explicit empty section.
60
+ * What stays fatal is silence WITH a reported cause, the masked-disconnect
61
+ * case `modelError` names outright.
69
62
  *
70
63
  * Returns null when the result is trustworthy.
71
64
  */
@@ -125,7 +118,7 @@ export function classifyResearchWorker(name, result) {
125
118
  // point of this branch is to tell them apart:
126
119
  //
127
120
  // FAILED, cause reported: pi delivers a failed turn as an empty assistant
128
- // message with stopReason "error" and exit 0, so the real cause used to be
121
+ // message with stopReason "error" and exit 0, so the real cause would be
129
122
  // discarded and reported as the useless "produced no output". Name it.
130
123
  // FAILED, child never spoke: no stdout at all means the child died before it
131
124
  // could run (unresolvable provider, missing key, bad argv) — it never
@@ -181,10 +174,9 @@ export function emptySectionBody(name) {
181
174
  }
182
175
  /**
183
176
  * A worker answer that IS the word "nothing" and carries no other content:
184
- * `(none)`, `N/A`, `- none`, `(no content)`, `(no entries)`. Live workers write
185
- * these often on a task that touches nothing (measured on the issue's prompt:
186
- * `(no content)`, `(no response)`, a bare `(none)` from the gate's own retry),
187
- * and each one means exactly what an empty answer means — so they are recorded
177
+ * `(none)`, `N/A`, `- none`, `(no content)`, `(no entries)`. A worker writes one of
178
+ * these on a task that touches nothing, and each means exactly what an empty
179
+ * answer means so they are recorded
188
180
  * with the same marker rather than passed through in whatever shape the model
189
181
  * happened to pick. Deliberately NARROW: it matches only a lone token, never
190
182
  * prose like "(no APIs to list — this task creates a plain HTML file …)", which
@@ -205,16 +197,9 @@ export function isBareNoneAnswer(text) {
205
197
  * fires on a normal project where the first attempt died for an unrelated reason,
206
198
  * and an easy opt-out there would silence real research.
207
199
  *
208
- * MEASUREMENT OPEN. The recovery path's QUALITY on a real repo is being measured
209
- * (scripts live under /home/edgars/tmp/issue10: first FILES answer faulted to
210
- * empty, every other child live, against an uninterrupted control). First rep on
211
- * an earlier wording did NOT take the `(none)` exit but drifted into writing code
212
- * instead of listing paths — the deliverable-not-inputs failure the base prompt
213
- * already forbids below this preamble. Blast radius is bounded: the gate fires
214
- * only on a run that would otherwise have FAILED outright, so a mediocre recovered
215
- * section is strictly better than the dead task it replaces — but if the drift
216
- * reproduces, this preamble must restate the section's output contract, not just
217
- * demand an answer.
200
+ * The blast radius is bounded: this gate fires only on a run that would otherwise
201
+ * have FAILED outright, so even a mediocre recovered section beats the dead task
202
+ * it replaces.
218
203
  */
219
204
  const EMPTY_SECTION_PREAMBLE = 'STOP. Your previous attempt returned an EMPTY answer — zero characters. An empty '
220
205
  + 'response cannot be accepted, because it is indistinguishable from a worker that '
@@ -247,23 +232,19 @@ export async function runResearchWorker(spec, run, prior = []) {
247
232
  const runOnce = (extraPreamble) => run.record(spec.label, run.runWorker(spec.label, {
248
233
  prompt: extraPreamble ? `${extraPreamble}\n\n${basePrompt}` : basePrompt,
249
234
  cwd: run.cwd,
235
+ contextWindow: run.contextWindow,
250
236
  signal: run.signal,
251
237
  spawn: run.spawn,
252
- // ONE CELL PER WORKER since 2026-08-28. They used to share
253
- // the `research` cell on the grounds that they are the same
254
- // job over four questions; the run logs disagree. All 40.7
255
- // wasted research minutes in mx5-n were restarts in
256
- // `tooling` and `context`, and `files`/`apis` never
257
- // restarted — so the level that pays for one pair is being
258
- // paid for the other. THE FOUR CELLS DO NOT SHIP IDENTICAL:
259
- // `research:files` is `off` on a measured tie while the
260
- // other three are `medium`, so this line changes what the
261
- // FILES worker runs at for every default-mode user. The
262
- // evidence is on each cell in reasoning.ts.
238
+ // ONE REASONING CELL PER WORKER, keyed on the spec's LABEL
239
+ // the four workers ask four different questions and the four
240
+ // cells do not ship identical. `REASONING_DEFAULTS` in
241
+ // config/reasoning.ts is where each one's level lives, so this
242
+ // line decides what THIS worker runs at for a default-mode
243
+ // user.
263
244
  thinking: run.thinkingFor(spec.label),
264
245
  ...(spec.tools ? { tools: spec.tools } : {}),
265
246
  ...(spec.extensions ? { extensions: spec.extensions } : {}),
266
- // The three 5B lever spreads that used to sit here are the
247
+ // The three lever spreads that would otherwise sit here are the
267
248
  // `research` row of WORKER_PROFILES (workers/worker-profiles.ts).
268
249
  // Two facts still come from here, and only these two: which of
269
250
  // the four workers is docs-capable (only it can be scaled), and
@@ -275,10 +256,8 @@ export async function runResearchWorker(spec, run, prior = []) {
275
256
  env: run.leverEnv
276
257
  },
277
258
  // One line per DISCARDED attempt. The `done` line below reports
278
- // the final attempt only, so a worker that timed out twice at
279
- // 240s and then answered used to log exactly like a clean one
280
- // 8 minutes of burned compute recoverable only by subtracting
281
- // its own wait+work from the start/done timestamps.
259
+ // the FINAL attempt only, so without these a worker that timed
260
+ // out twice and then answered logs exactly like a clean one.
282
261
  onCarryForward: ci => {
283
262
  run.logDebug?.(`${spec.label}: CARRY-FORWARD injected into attempt ${ci.attempt}`
284
263
  + ` (${ci.chars} chars onto a ${ci.promptCharsBefore}-char prompt)`);
@@ -286,6 +265,7 @@ export async function runResearchWorker(spec, run, prior = []) {
286
265
  onRestart: rs => {
287
266
  run.logDebug?.(`${spec.label}: RESTART (attempt ${rs.attempt} discarded)`
288
267
  + ` reason=${rs.reason} wall=${rs.wallMs}ms`
268
+ + ` discarded=${rs.partialChars}ch`
289
269
  + ` wait=${rs.waitMs}ms work=${rs.workMs}ms`
290
270
  + (rs.detail ? ` — ${rs.detail}` : ''));
291
271
  run.onChildOutput?.(`${spec.label}: restart (${rs.reason})`);
@@ -299,8 +279,8 @@ export async function runResearchWorker(spec, run, prior = []) {
299
279
  }
300
280
  }));
301
281
  let r = await runOnce();
302
- // EMPTY-SECTION GATE (issue #10). A worker that returns zero bytes on a clean run
303
- // used to fail the whole task ("Research APIS worker produced no output"), which is
282
+ // EMPTY-SECTION GATE. A worker that returns zero bytes on a clean run
283
+ // would fail the whole task ("Research APIS worker produced no output"), which is
304
284
  // exactly what an extremely simple task provokes: with nothing on disk to survey and
305
285
  // no external symbol in play, silence is the correct answer and the run died on it.
306
286
  // Retry ONCE — silence is genuinely ambiguous, and a worker that crashed before
@@ -394,11 +374,9 @@ export async function runResearchWorker(spec, run, prior = []) {
394
374
  + (r.restarts.length > 0 ?
395
375
  ` restarts=[${r.restarts.map(x => x.reason).join(',')}]`
396
376
  : '')
397
- // Attribution for the RESCUE arm: a run with zero restarts was
398
- // never killed (the progress deadline did it), while a run that
399
- // restarted and salvaged was killed but kept its work. Without
400
- // this the two are indistinguishable in the logs, and "0
401
- // timeouts" cannot be traced to the half that earned it.
377
+ // A run with zero restarts was never killed; a run that restarted
378
+ // and salvaged WAS killed but kept its work. Without this flag the
379
+ // two are indistinguishable in the log.
402
380
  + (r.salvagedFromDiscardedAttempt ? ' salvaged=1' : '')
403
381
  + (r.stderr ? ` stderr=${r.stderr.slice(0, 300)}` : '')
404
382
  + (r.leakedToolCall ? ` leaked=${r.leakedToolCall.trim().slice(0, 80)}` : ''));
@@ -1,25 +1,24 @@
1
1
  /**
2
2
  * Resume gating & the honest resume banner.
3
3
  *
4
- * mx5 run 14 lost ~10 hours to dead air that looked like a stall: the host was
5
- * powered off overnight, both containers stopped at 20:00Z, and the run picked
6
- * up cleanly the moment they were restarted at 06:01Z. Nothing was wrong with
7
- * the run — the only defect was that nobody could tell. Two things follow.
4
+ * A host that is powered off mid-run produces dead air that looks exactly like a
5
+ * stall. Nothing is wrong with the run; the defect is that nobody can tell. Two
6
+ * things follow.
8
7
  *
9
- * (1) A restart-time resume can be automated (`restart: unless-stopped` on the
10
- * containers plus a boot hook that runs `/task-auto-resume --unattended`), which
11
- * turns that 10h of nothing into minutes.
8
+ * (1) A restart-time resume can be automated a boot hook that runs
9
+ * `/task-auto-resume --unattended` (the flag auto-orchestrator.ts parses).
12
10
  *
13
11
  * (2) An automated resume must not resume everything. `RESUMABLE_STATES`
14
- * deliberately includes `failed` and `cancelled` because a HUMAN typing
15
- * /task-auto-resume has decided to continue; a boot hook has decided nothing. A
16
- * failed run stopped for a reason a power cycle does not clear, and re-entering
17
- * it unattended burns the whole loop against the same wall. Unattended resume
18
- * therefore covers in-flight states only (see UNATTENDED_STATES), and refuses
19
- * the rest by name rather than silently doing nothing.
12
+ * (task-types.ts) deliberately includes `failed` and `cancelled`, because a HUMAN
13
+ * typing /task-auto-resume has decided to continue; a boot hook has decided
14
+ * nothing. A failed run stopped for a reason a power cycle does not clear, and
15
+ * re-entering it unattended burns the whole loop against the same wall.
16
+ * Unattended resume therefore covers in-flight states only (see
17
+ * UNATTENDED_STATES), and refuses the rest BY NAME rather than silently doing
18
+ * nothing.
20
19
  *
21
- * The banner follows the honest-restart-hint rule (70a8497): say exactly what
22
- * was observed and exactly what it does not tell you. All this process knows is
20
+ * The banner says exactly what was observed and exactly what it does not tell
21
+ * you. All this process knows is
23
22
  * when the AUTO file was last written — it cannot distinguish a stopped host
24
23
  * from a hung child from a slow task, so it reports the gap and attributes no
25
24
  * cause. It is equally careful about the tree: nothing is rolled back between
@@ -87,10 +87,11 @@ export interface MergedRepair {
87
87
  verifyCommand?: string;
88
88
  }
89
89
  /**
90
- * Collapse candidates by file — MANDATORY dedup: run 14's two teardown.ts debts
91
- * (TASK_0013, TASK_0019) must yield exactly ONE repair task naming both. First
92
- * record wins for defect/command (they describe the same fault); blamed tasks
93
- * accumulate in first-seen order.
90
+ * Collapse candidates by file — MANDATORY dedup: two debts naming the same file
91
+ * must yield exactly ONE repair task naming both blamed tasks. Keyed on the
92
+ * NORMALISED path, so `./x.ts` and `x.ts` are one entry. First record wins for
93
+ * defect and command (they describe the same fault); blamed tasks accumulate in
94
+ * first-seen order.
94
95
  */
95
96
  export declare function mergeRepairCandidates(candidates: RepairCandidate[]): MergedRepair[];
96
97
  /** Machine-recognisable prefix, so a repair entry can be found in a plan again. */
@@ -112,11 +113,10 @@ export declare function parseRepairTitleFile(title: string): string | null;
112
113
  */
113
114
  export declare function planHasRepairFor(titles: string[], file: string): boolean;
114
115
  /**
115
- * The extra scope fence a repair entry carries into refine. Without it, refine
116
- * re-expands "repair test/teardown.ts: parameterized table names in TRUNCATE"
117
- * into "overhaul the test infrastructure" the /task-auto drift lesson. The
118
- * fence pins the single editable file and pins the VERIFY to the exact command
119
- * the debt failed on.
116
+ * The extra scope fence a repair entry carries into refine. A repair title names
117
+ * one file and one narrow defect, which refine will otherwise re-expand into
118
+ * "overhaul the test infrastructure". The fence pins the single editable file and
119
+ * pins the VERIFY to the exact command the debt failed on.
120
120
  */
121
121
  export declare function buildRepairScopeFence(file: string, verifyCommand?: string): string;
122
122
  export {};