@mjasnikovs/pi-task 0.38.29 → 0.38.31

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 (373) 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/index.js +2 -0
  14. package/dist/remote/bridge.d.ts +19 -10
  15. package/dist/remote/bridge.js +3 -2
  16. package/dist/remote/broadcast.js +3 -1
  17. package/dist/remote/events.js +12 -11
  18. package/dist/remote/history.d.ts +1 -1
  19. package/dist/remote/protocol.d.ts +6 -3
  20. package/dist/remote/protocol.js +2 -1
  21. package/dist/remote/push.d.ts +16 -16
  22. package/dist/remote/push.js +27 -27
  23. package/dist/remote/register.d.ts +3 -3
  24. package/dist/remote/register.js +17 -19
  25. package/dist/remote/server.d.ts +9 -8
  26. package/dist/remote/server.js +15 -14
  27. package/dist/remote/session-state.d.ts +5 -4
  28. package/dist/remote/session-state.js +8 -5
  29. package/dist/remote/sw.d.ts +7 -6
  30. package/dist/remote/sw.js +7 -6
  31. package/dist/remote/tailscale.d.ts +4 -2
  32. package/dist/remote/tailscale.js +4 -2
  33. package/dist/remote/ui-highlight.js +6 -5
  34. package/dist/remote/ui-render.js +4 -4
  35. package/dist/remote/ui-script.js +24 -24
  36. package/dist/remote/ui-styles.d.ts +1 -1
  37. package/dist/remote/ui-styles.js +10 -13
  38. package/dist/remote/ui-tools.js +9 -6
  39. package/dist/shared/child-extensions.d.ts +29 -17
  40. package/dist/shared/child-extensions.js +29 -17
  41. package/dist/shared/child-output.d.ts +30 -24
  42. package/dist/shared/child-output.js +25 -17
  43. package/dist/shared/child-process.d.ts +47 -40
  44. package/dist/shared/child-process.js +50 -59
  45. package/dist/shared/command-watchdog.d.ts +85 -16
  46. package/dist/shared/command-watchdog.js +115 -21
  47. package/dist/shared/fs-text.d.ts +16 -10
  48. package/dist/shared/fs-text.js +16 -10
  49. package/dist/shared/git-runner.d.ts +25 -25
  50. package/dist/shared/git-runner.js +25 -25
  51. package/dist/shared/leaked-tool-call.d.ts +17 -11
  52. package/dist/shared/leaked-tool-call.js +23 -15
  53. package/dist/shared/model-endpoint.d.ts +29 -16
  54. package/dist/shared/model-endpoint.js +33 -21
  55. package/dist/shared/pi-invocation.d.ts +7 -4
  56. package/dist/shared/pi-invocation.js +12 -7
  57. package/dist/shared/pkg-version.d.ts +13 -5
  58. package/dist/shared/pkg-version.js +13 -5
  59. package/dist/shared/reasoning-capability.d.ts +35 -24
  60. package/dist/shared/reasoning-capability.js +35 -24
  61. package/dist/shared/stream-watchdog.d.ts +60 -44
  62. package/dist/shared/stream-watchdog.js +62 -45
  63. package/dist/task/accept-debt.d.ts +41 -43
  64. package/dist/task/accept-debt.js +73 -65
  65. package/dist/task/api-synthesis.d.ts +24 -21
  66. package/dist/task/api-synthesis.js +32 -26
  67. package/dist/task/apis-contract.d.ts +32 -64
  68. package/dist/task/apis-contract.js +32 -64
  69. package/dist/task/artifact-closure.d.ts +27 -13
  70. package/dist/task/artifact-closure.js +95 -67
  71. package/dist/task/auto-commit.d.ts +46 -35
  72. package/dist/task/auto-commit.js +51 -38
  73. package/dist/task/auto-io.d.ts +45 -25
  74. package/dist/task/auto-io.js +57 -29
  75. package/dist/task/auto-orchestrator.d.ts +26 -24
  76. package/dist/task/auto-orchestrator.js +192 -165
  77. package/dist/task/auto-prompts.d.ts +36 -24
  78. package/dist/task/auto-prompts.js +40 -26
  79. package/dist/task/autofix-ledger.d.ts +27 -25
  80. package/dist/task/autofix-ledger.js +29 -26
  81. package/dist/task/batch-test-task.d.ts +20 -12
  82. package/dist/task/batch-test-task.js +67 -60
  83. package/dist/task/boot-probe.d.ts +60 -44
  84. package/dist/task/boot-probe.js +91 -72
  85. package/dist/task/cancel-input.d.ts +30 -16
  86. package/dist/task/cancel-input.js +20 -11
  87. package/dist/task/cancel-points.d.ts +27 -20
  88. package/dist/task/cancel-points.js +30 -22
  89. package/dist/task/child-runner.d.ts +124 -55
  90. package/dist/task/child-runner.js +298 -90
  91. package/dist/task/child-status.d.ts +23 -16
  92. package/dist/task/child-status.js +23 -16
  93. package/dist/task/clamp-output.js +12 -5
  94. package/dist/task/command-run.d.ts +31 -28
  95. package/dist/task/command-run.js +44 -35
  96. package/dist/task/command-shrink.d.ts +25 -18
  97. package/dist/task/command-shrink.js +37 -31
  98. package/dist/task/command-watchdog.d.ts +9 -6
  99. package/dist/task/command-watchdog.js +21 -15
  100. package/dist/task/context-attribution.d.ts +34 -26
  101. package/dist/task/context-attribution.js +34 -26
  102. package/dist/task/context-silence.d.ts +39 -29
  103. package/dist/task/context-silence.js +35 -25
  104. package/dist/task/context-usage.d.ts +16 -9
  105. package/dist/task/context-usage.js +16 -9
  106. package/dist/task/contracts.d.ts +8 -4
  107. package/dist/task/contracts.js +25 -17
  108. package/dist/task/coverage-loop.d.ts +22 -18
  109. package/dist/task/coverage-loop.js +35 -30
  110. package/dist/task/critique-probes.d.ts +13 -14
  111. package/dist/task/critique-probes.js +50 -39
  112. package/dist/task/debug-log.d.ts +13 -5
  113. package/dist/task/debug-log.js +32 -20
  114. package/dist/task/decompose-fidelity.d.ts +11 -9
  115. package/dist/task/decompose-fidelity.js +38 -33
  116. package/dist/task/decompose-granularity.d.ts +41 -38
  117. package/dist/task/decompose-granularity.js +41 -38
  118. package/dist/task/deep-render-check.d.ts +22 -14
  119. package/dist/task/deep-render-check.js +40 -31
  120. package/dist/task/dropped-input.d.ts +12 -7
  121. package/dist/task/dropped-input.js +5 -2
  122. package/dist/task/enforce-attribution.d.ts +38 -47
  123. package/dist/task/enforce-attribution.js +46 -52
  124. package/dist/task/enforce-guidelines.d.ts +31 -20
  125. package/dist/task/enforce-guidelines.js +32 -21
  126. package/dist/task/enrichment.d.ts +7 -2
  127. package/dist/task/enrichment.js +26 -14
  128. package/dist/task/env-notes.d.ts +16 -7
  129. package/dist/task/env-notes.js +48 -31
  130. package/dist/task/env-template-closure.d.ts +4 -4
  131. package/dist/task/env-template-closure.js +42 -34
  132. package/dist/task/external-context.d.ts +28 -21
  133. package/dist/task/external-context.js +17 -12
  134. package/dist/task/failure-classifier.d.ts +4 -5
  135. package/dist/task/failure-classifier.js +30 -8
  136. package/dist/task/file-inventory.d.ts +15 -11
  137. package/dist/task/file-inventory.js +25 -22
  138. package/dist/task/final-gate-fix.d.ts +74 -86
  139. package/dist/task/final-gate-fix.js +97 -116
  140. package/dist/task/final-gate-progress.d.ts +29 -46
  141. package/dist/task/final-gate-progress.js +40 -51
  142. package/dist/task/final-gate.d.ts +64 -97
  143. package/dist/task/final-gate.js +192 -199
  144. package/dist/task/fix-child.d.ts +21 -27
  145. package/dist/task/fix-child.js +21 -27
  146. package/dist/task/foreign-path.d.ts +6 -5
  147. package/dist/task/foreign-path.js +0 -0
  148. package/dist/task/frozen-conflict.d.ts +9 -10
  149. package/dist/task/frozen-conflict.js +61 -64
  150. package/dist/task/frozen-path-guard.d.ts +35 -14
  151. package/dist/task/frozen-path-guard.js +56 -39
  152. package/dist/task/gate-child.d.ts +27 -28
  153. package/dist/task/gate-child.js +36 -35
  154. package/dist/task/gate-deps.d.ts +34 -27
  155. package/dist/task/gate-deps.js +169 -159
  156. package/dist/task/gate-tally.d.ts +77 -80
  157. package/dist/task/gate-tally.js +65 -68
  158. package/dist/task/git-state-guard.d.ts +15 -11
  159. package/dist/task/git-state-guard.js +76 -66
  160. package/dist/task/impl-widget.d.ts +25 -16
  161. package/dist/task/impl-widget.js +27 -17
  162. package/dist/task/implementation-guards.d.ts +26 -0
  163. package/dist/task/implementation-guards.js +177 -0
  164. package/dist/task/implementation-thinking.d.ts +33 -31
  165. package/dist/task/implementation-thinking.js +5 -6
  166. package/dist/task/implementation-turn.d.ts +39 -31
  167. package/dist/task/implementation-turn.js +41 -28
  168. package/dist/task/inline-markdown.d.ts +20 -7
  169. package/dist/task/inline-markdown.js +15 -6
  170. package/dist/task/launch-config-gap.js +25 -39
  171. package/dist/task/launch-contract.d.ts +18 -21
  172. package/dist/task/launch-contract.js +28 -30
  173. package/dist/task/launch-manifest.d.ts +6 -2
  174. package/dist/task/launch-manifest.js +35 -34
  175. package/dist/task/ledger.js +16 -14
  176. package/dist/task/lint-fix.d.ts +6 -8
  177. package/dist/task/lint-fix.js +67 -69
  178. package/dist/task/loop-detector.d.ts +27 -8
  179. package/dist/task/loop-detector.js +38 -14
  180. package/dist/task/mid-run-input.d.ts +17 -15
  181. package/dist/task/mid-run-input.js +17 -15
  182. package/dist/task/orchestrator.d.ts +24 -28
  183. package/dist/task/orchestrator.js +89 -66
  184. package/dist/task/orientation.d.ts +18 -23
  185. package/dist/task/orientation.js +24 -31
  186. package/dist/task/owned-freeze-conflict.d.ts +21 -20
  187. package/dist/task/owned-freeze-conflict.js +52 -85
  188. package/dist/task/owned-freeze-reassign.d.ts +40 -60
  189. package/dist/task/owned-freeze-reassign.js +41 -61
  190. package/dist/task/parsers.d.ts +4 -2
  191. package/dist/task/parsers.js +4 -4
  192. package/dist/task/phases.d.ts +41 -48
  193. package/dist/task/phases.js +196 -252
  194. package/dist/task/plan-io.d.ts +6 -7
  195. package/dist/task/plan-io.js +6 -7
  196. package/dist/task/plan-orchestrator.d.ts +10 -8
  197. package/dist/task/plan-orchestrator.js +14 -10
  198. package/dist/task/plan-prompts.d.ts +6 -5
  199. package/dist/task/plan-prompts.js +6 -5
  200. package/dist/task/plan-readonly.d.ts +4 -5
  201. package/dist/task/plan-readonly.js +4 -5
  202. package/dist/task/plan-rounds.d.ts +17 -29
  203. package/dist/task/plan-rounds.js +21 -34
  204. package/dist/task/plan-session.d.ts +58 -72
  205. package/dist/task/plan-session.js +61 -83
  206. package/dist/task/probe-gaming.d.ts +28 -27
  207. package/dist/task/probe-gaming.js +0 -0
  208. package/dist/task/prohibition-probe.d.ts +14 -16
  209. package/dist/task/prompts.d.ts +3 -4
  210. package/dist/task/prompts.js +17 -26
  211. package/dist/task/qa-transcript.d.ts +15 -22
  212. package/dist/task/qa-transcript.js +15 -21
  213. package/dist/task/question-box.d.ts +17 -13
  214. package/dist/task/question-box.js +19 -15
  215. package/dist/task/question-dedup.d.ts +6 -7
  216. package/dist/task/question-dedup.js +13 -14
  217. package/dist/task/question-dialog.d.ts +22 -32
  218. package/dist/task/question-dialog.js +22 -32
  219. package/dist/task/question-source.d.ts +18 -44
  220. package/dist/task/question-source.js +22 -51
  221. package/dist/task/refuted-constraint.d.ts +11 -31
  222. package/dist/task/refuted-constraint.js +27 -51
  223. package/dist/task/regenerable-artifacts.d.ts +12 -31
  224. package/dist/task/regenerable-artifacts.js +12 -31
  225. package/dist/task/render-check.d.ts +11 -22
  226. package/dist/task/render-check.js +33 -46
  227. package/dist/task/repo-health-check.d.ts +10 -14
  228. package/dist/task/repo-health-check.js +17 -23
  229. package/dist/task/requirements.d.ts +38 -71
  230. package/dist/task/requirements.js +78 -126
  231. package/dist/task/research-fanout-budget.d.ts +51 -88
  232. package/dist/task/research-fanout-budget.js +51 -88
  233. package/dist/task/research-worker.d.ts +29 -39
  234. package/dist/task/research-worker.js +37 -61
  235. package/dist/task/resume-gap.d.ts +14 -15
  236. package/dist/task/root-cause-repair.d.ts +9 -9
  237. package/dist/task/root-cause-repair.js +28 -40
  238. package/dist/task/run-bracket.d.ts +10 -13
  239. package/dist/task/run-end.d.ts +12 -22
  240. package/dist/task/run-end.js +8 -16
  241. package/dist/task/run-final-gate.d.ts +19 -21
  242. package/dist/task/run-final-gate.js +62 -80
  243. package/dist/task/runner-globs.d.ts +12 -13
  244. package/dist/task/runner-globs.js +12 -13
  245. package/dist/task/runner-resolve.d.ts +9 -9
  246. package/dist/task/runner-resolve.js +22 -23
  247. package/dist/task/script-escape.d.ts +10 -12
  248. package/dist/task/script-escape.js +13 -14
  249. package/dist/task/serve-entry.d.ts +1 -1
  250. package/dist/task/serve-entry.js +22 -25
  251. package/dist/task/service-blocks.js +4 -2
  252. package/dist/task/shipped-source.d.ts +11 -29
  253. package/dist/task/shipped-source.js +11 -29
  254. package/dist/task/skip-escape.js +10 -14
  255. package/dist/task/spec-urls.d.ts +26 -65
  256. package/dist/task/spec-urls.js +26 -65
  257. package/dist/task/spec-validation.d.ts +17 -20
  258. package/dist/task/spec-validation.js +17 -20
  259. package/dist/task/stall-detector.d.ts +23 -30
  260. package/dist/task/stall-detector.js +23 -30
  261. package/dist/task/stream-watchdog.d.ts +14 -12
  262. package/dist/task/stream-watchdog.js +14 -12
  263. package/dist/task/substitution-probe.d.ts +17 -20
  264. package/dist/task/substitution-probe.js +17 -20
  265. package/dist/task/task-gates.d.ts +36 -41
  266. package/dist/task/task-gates.js +95 -106
  267. package/dist/task/task-io.d.ts +4 -4
  268. package/dist/task/task-io.js +4 -4
  269. package/dist/task/task-parsers.js +4 -3
  270. package/dist/task/task-provenance.d.ts +2 -2
  271. package/dist/task/task-provenance.js +11 -13
  272. package/dist/task/task-types.d.ts +4 -3
  273. package/dist/task/terminal-outcome.d.ts +14 -16
  274. package/dist/task/terminal-outcome.js +12 -14
  275. package/dist/task/test-assembly.d.ts +13 -20
  276. package/dist/task/test-assembly.js +13 -20
  277. package/dist/task/timings.d.ts +5 -3
  278. package/dist/task/timings.js +5 -3
  279. package/dist/task/title-label.d.ts +9 -4
  280. package/dist/task/title-label.js +9 -4
  281. package/dist/task/type-only-answer.d.ts +44 -52
  282. package/dist/task/type-only-answer.js +44 -52
  283. package/dist/task/unfailable-command.d.ts +18 -24
  284. package/dist/task/unfailable-command.js +21 -27
  285. package/dist/task/unknown-routing.d.ts +10 -4
  286. package/dist/task/unknown-routing.js +10 -4
  287. package/dist/task/user-directives.d.ts +5 -8
  288. package/dist/task/user-directives.js +5 -8
  289. package/dist/task/verify-quality.d.ts +18 -22
  290. package/dist/task/verify-quality.js +45 -46
  291. package/dist/task/verify-reconcile.d.ts +15 -10
  292. package/dist/task/verify-reconcile.js +45 -43
  293. package/dist/task/verify-resolution.d.ts +24 -20
  294. package/dist/task/verify-resolution.js +51 -50
  295. package/dist/task/verify-work.d.ts +59 -66
  296. package/dist/task/verify-work.js +101 -138
  297. package/dist/task/widget.d.ts +15 -14
  298. package/dist/task/widget.js +22 -17
  299. package/dist/task/wiring-claims.d.ts +25 -32
  300. package/dist/task/wiring-claims.js +30 -35
  301. package/dist/task/write-guard.d.ts +39 -39
  302. package/dist/task/write-guard.js +48 -51
  303. package/dist/task/yolo.d.ts +34 -30
  304. package/dist/task/yolo.js +42 -37
  305. package/dist/workers/abstention.d.ts +21 -41
  306. package/dist/workers/abstention.js +27 -48
  307. package/dist/workers/brave-search.d.ts +4 -3
  308. package/dist/workers/brave-search.js +5 -2
  309. package/dist/workers/brave-warning.d.ts +7 -4
  310. package/dist/workers/brave-warning.js +19 -7
  311. package/dist/workers/ddg-search.d.ts +6 -6
  312. package/dist/workers/ddg-search.js +18 -12
  313. package/dist/workers/docs-cache.js +5 -2
  314. package/dist/workers/docs-chunk.d.ts +30 -37
  315. package/dist/workers/docs-chunk.js +37 -41
  316. package/dist/workers/docs-core.d.ts +28 -44
  317. package/dist/workers/docs-core.js +25 -44
  318. package/dist/workers/docs-index.js +4 -3
  319. package/dist/workers/docs-lookup.d.ts +15 -22
  320. package/dist/workers/docs-lookup.js +12 -21
  321. package/dist/workers/docs-project.d.ts +15 -9
  322. package/dist/workers/docs-project.js +17 -10
  323. package/dist/workers/docs-resolve.d.ts +19 -20
  324. package/dist/workers/docs-resolve.js +35 -32
  325. package/dist/workers/docs-retrieve.d.ts +5 -6
  326. package/dist/workers/docs-retrieve.js +18 -15
  327. package/dist/workers/exa-search.d.ts +9 -6
  328. package/dist/workers/exa-search.js +23 -12
  329. package/dist/workers/fetch-core.d.ts +13 -16
  330. package/dist/workers/fetch-core.js +23 -23
  331. package/dist/workers/focused-extractor.d.ts +13 -12
  332. package/dist/workers/focused-extractor.js +27 -19
  333. package/dist/workers/html-clean.js +24 -14
  334. package/dist/workers/http-request.d.ts +28 -20
  335. package/dist/workers/http-request.js +22 -17
  336. package/dist/workers/npm-version.d.ts +28 -11
  337. package/dist/workers/npm-version.js +24 -15
  338. package/dist/workers/phantom-imports.d.ts +15 -12
  339. package/dist/workers/phantom-imports.js +30 -24
  340. package/dist/workers/pi-worker-core.d.ts +65 -96
  341. package/dist/workers/pi-worker-core.js +93 -181
  342. package/dist/workers/pi-worker-docs.d.ts +24 -19
  343. package/dist/workers/pi-worker-docs.js +67 -76
  344. package/dist/workers/pi-worker-fetch.d.ts +7 -3
  345. package/dist/workers/pi-worker-fetch.js +27 -19
  346. package/dist/workers/pi-worker-search.js +12 -8
  347. package/dist/workers/pi-worker.d.ts +9 -4
  348. package/dist/workers/pi-worker.js +21 -14
  349. package/dist/workers/reasoning-warning.d.ts +18 -17
  350. package/dist/workers/reasoning-warning.js +22 -20
  351. package/dist/workers/research-cache.js +50 -78
  352. package/dist/workers/search-core.js +7 -5
  353. package/dist/workers/search-types.d.ts +10 -9
  354. package/dist/workers/search-types.js +9 -8
  355. package/dist/workers/session-hint.d.ts +13 -14
  356. package/dist/workers/session-hint.js +8 -9
  357. package/dist/workers/shared.d.ts +21 -25
  358. package/dist/workers/shared.js +0 -0
  359. package/dist/workers/single-read-extension.d.ts +14 -7
  360. package/dist/workers/single-read-extension.js +14 -7
  361. package/dist/workers/single-read-guard.d.ts +27 -30
  362. package/dist/workers/single-read-guard.js +36 -36
  363. package/dist/workers/typeonly-log.d.ts +12 -9
  364. package/dist/workers/typeonly-log.js +29 -33
  365. package/dist/workers/worker-channels.d.ts +15 -23
  366. package/dist/workers/worker-channels.js +15 -23
  367. package/dist/workers/worker-failure.d.ts +38 -46
  368. package/dist/workers/worker-failure.js +31 -39
  369. package/dist/workers/worker-kill.d.ts +25 -26
  370. package/dist/workers/worker-kill.js +16 -19
  371. package/dist/workers/worker-profiles.d.ts +54 -56
  372. package/dist/workers/worker-profiles.js +63 -39
  373. package/package.json +10 -8
@@ -2,41 +2,43 @@
2
2
  * Hold the host session at the `implementation` group's thinking level for the
3
3
  * duration of one implementation turn, then put it back.
4
4
  *
5
- * WHY THIS IS NOT LIKE THE OTHER SIX GROUPS
6
- * -----------------------------------------
7
- * Every other group is a child process, so its level is one argv flag and it
8
- * dies with the child. The implementation turn runs in the USER'S OWN session
9
- * (orchestrator.ts `sendSpec` sendUserMessage superviseImplementation), so
10
- * the only lever is `pi.setThinkingLevel`, which is session-global and visible.
5
+ * WHY THIS GROUP IS NOT LIKE THE OTHERS
6
+ * -------------------------------------
7
+ * Every other reasoning group runs in a child process, so its level is one argv
8
+ * flag (`--thinking <level>`, built in reasoning-args.ts) and it dies with the
9
+ * child. The implementation turn runs in the USER'S OWN session
10
+ * (orchestrator.ts `sendSpec` -> `sendUserMessage` -> `superviseImplementation`),
11
+ * so the only lever is `pi.setThinkingLevel`, which is session-global.
11
12
  *
12
- * THREE THINGS pi DOES that this has to survive. All three read from
13
- * pi-coding-agent's agent-session `setThinkingLevel`:
13
+ * THREE THINGS pi DOES that this has to survive:
14
14
  *
15
- * 1. IT PERSISTS. On a real change it calls
16
- * `settingsManager.setDefaultThinkingLevel(...)`, writing
17
- * `~/.pi/agent/settings.json`. This is not a session-local toggle without
18
- * the restore, running one task would silently rewrite the user's global
19
- * default. That makes `release()` load-bearing, not tidy-up.
20
- * 2. IT CLAMPS, to what the model declares it supports. We may ask for `medium`
21
- * and be given `off`. So the restore writes back what was READ after
22
- * setting, never what was asked for otherwise a clamp would ratchet the
23
- * stored default a little further every run.
24
- * 3. IT IS OBSERVABLE, and the user can change it mid-turn (shift+tab cycles
25
- * the level). Restoring blindly would clobber a choice they just made. We
26
- * detect it by comparing the live level at release against what we applied:
27
- * if it has moved, somebody else moved it, and we leave it alone.
15
+ * 1. IT PERSISTS. pi-coding-agent's agent-session `setThinkingLevel` calls
16
+ * `settingsManager.setDefaultThinkingLevel(...)` whenever the effective
17
+ * level actually changes, and that writes pi's global settings file
18
+ * (`~/.pi/agent/settings.json`). Without the restore, running one task would
19
+ * silently rewrite the user's global default. That makes `release()`
20
+ * load-bearing, not tidy-up.
21
+ * 2. IT CLAMPS, to the levels the model declares. A model with no reasoning
22
+ * support offers only `off`, so asking for `medium` yields `off`. The
23
+ * restore therefore writes back what was READ after setting, never what was
24
+ * asked for otherwise a clamp would ratchet the stored default a little
25
+ * further every run.
26
+ * 3. IT IS OBSERVABLE, and the user can change it mid-turn: `shift+tab` is the
27
+ * default binding for `app.thinking.cycle`, and a change invalidates the
28
+ * footer. Restoring blindly would clobber a choice they just made. We detect
29
+ * it by comparing the live level at release against what we applied: if it
30
+ * has moved, somebody else moved it, and we leave it alone.
28
31
  *
29
- * We compare rather than subscribe because `pi.on` returns no unsubscribe
30
- * handle, so a per-turn listener could only ever be added, never removed. The
31
- * comparison answers the same question with no accumulating state.
32
+ * We compare rather than subscribe because the extension API's `on(...)` returns
33
+ * `void` — there is no unsubscribe handle so a per-turn listener could only
34
+ * ever be added, never removed. The comparison answers the same question with no
35
+ * accumulating state.
32
36
  */
33
37
  import type { ThinkingLevel } from '@earendil-works/pi-agent-core';
34
38
  import { type GroupSetting } from '../config/reasoning.js';
35
39
  /**
36
- * The slice of the extension API this needs, named so tests can drive it without
37
- * a live pi session. Every other dependency in `RunSingleTaskOptions` is
38
- * injectable; this one has to be too, or the restore logic is only exercisable
39
- * by running a real task.
40
+ * The slice of the extension API this needs, named so tests can drive the
41
+ * hold-and-restore with a fake object instead of a live pi session.
40
42
  */
41
43
  export interface ThinkingControl {
42
44
  get(): ThinkingLevel;
@@ -47,8 +49,8 @@ export interface ThinkingControl {
47
49
  * that puts it back. Always call the returned function — `finally`, not the
48
50
  * happy path.
49
51
  *
50
- * `inherit` makes NO call at all, not even a redundant set-to-current: a set
51
- * that happens to be a no-op still goes through pi's change detection, and the
52
- * shipped default must not touch the user's settings file.
52
+ * `inherit` makes NO call at all, not even a redundant set-to-current. It means
53
+ * the same thing here as in `thinkingArgs`, which emits no `--thinking` flag for
54
+ * it: leave the level wherever it already is.
53
55
  */
54
56
  export declare function holdImplementationThinking(control: ThinkingControl, setting?: GroupSetting): () => void;
@@ -5,9 +5,9 @@ import { resolveReasoning } from '../config/reasoning.js';
5
5
  * that puts it back. Always call the returned function — `finally`, not the
6
6
  * happy path.
7
7
  *
8
- * `inherit` makes NO call at all, not even a redundant set-to-current: a set
9
- * that happens to be a no-op still goes through pi's change detection, and the
10
- * shipped default must not touch the user's settings file.
8
+ * `inherit` makes NO call at all, not even a redundant set-to-current. It means
9
+ * the same thing here as in `thinkingArgs`, which emits no `--thinking` flag for
10
+ * it: leave the level wherever it already is.
11
11
  */
12
12
  export function holdImplementationThinking(control, setting = resolveReasoning('implementation', getConfig())) {
13
13
  if (setting === 'inherit')
@@ -21,9 +21,8 @@ export function holdImplementationThinking(control, setting = resolveReasoning('
21
21
  return () => { };
22
22
  let released = false;
23
23
  return () => {
24
- // Idempotent: the caller's `finally` may run alongside an outer one on an
25
- // abort path, and a second restore would fight a user change made in
26
- // between.
24
+ // Idempotent by contract: only the first call restores. A later call
25
+ // would write `before` on top of whatever the level is by then.
27
26
  if (released)
28
27
  return;
29
28
  released = true;
@@ -7,12 +7,13 @@
7
7
  * • `aborted` — a user ESC (or the command watchdog) cut the turn short;
8
8
  * • `compaction` — a threshold auto-compaction parked the turn at idle without
9
9
  * auto-continuing (the runtime expects a manual continue);
10
- * • `error` — the model/provider died mid-turn after pi's own retries;
10
+ * • `error` — the model or provider failed after pi exhausted the retries
11
+ * in its own retry settings;
11
12
  * • `stop` — genuine completion.
12
- * `classifyTurnEnd` reads the session entries and names ONE of those, in the
13
- * precedence the supervision sequence needs; `superviseImplementation` then
14
- * resumes across compactions, lets the user steer after an interrupt, and reports
15
- * the terminal outcome. The orchestrator calls it once.
13
+ * `classifyTurnEnd` reads the session entries and names ONE of those;
14
+ * `superviseImplementation` then resumes across compactions, lets the user steer
15
+ * after an interrupt, and reports the terminal outcome. The orchestrator calls it
16
+ * from one place, inside the `sendSpec` closure.
16
17
  */
17
18
  import type { ExtensionCommandContext } from '@earendil-works/pi-coding-agent';
18
19
  /** How the most recent implementation turn ended. See {@link classifyTurnEnd}. */
@@ -34,21 +35,20 @@ export type SessionEntryLike = {
34
35
  /**
35
36
  * Classify how the most recent turn ended, from the session entries alone.
36
37
  *
37
- * Precedence, when several signals are present at once (this is the order the
38
- * supervision sequence has always applied, now stated in one place):
38
+ * Precedence, when several signals are present at once:
39
39
  * 1. `aborted` — the last assistant message has stopReason "aborted". A user
40
40
  * ESC (or watchdog abort) wins over everything: it is not a
41
41
  * compaction pause, and the steer loop owns it.
42
42
  * 2. `compaction` — a `compaction` entry sits AFTER the last assistant message.
43
- * Position-based, not timestamp-based: the runtime appends the
44
- * boundary to the tail of the branch after the message that
45
- * triggered it (`appendCompaction` `_appendEntry` push), so a
43
+ * Position-based, not timestamp-based: `appendCompaction`
44
+ * pushes the boundary onto the tail of the entry list, and
45
+ * `getEntries()` returns that list in append order, so a
46
46
  * trailing compaction means we are parked with no continuation.
47
- * A finished turn ends on an assistant message; an *overflow*
48
- * compaction self-retries and never leaves us idle here.
49
- * 3. `error` — the last assistant message has stopReason "error": the
50
- * model/provider died (context-overflow 400, disconnect, 5xx)
51
- * after pi exhausted its own retries.
47
+ * A finished turn ends on an assistant message. An overflow
48
+ * compaction that is going to retry continues the turn itself
49
+ * and never reaches us idle.
50
+ * 3. `error` — the last assistant message has stopReason "error": the model
51
+ * or provider failed after pi exhausted its own retries.
52
52
  * 4. `stop` — anything else, including a session with no assistant turn.
53
53
  */
54
54
  export declare function classifyTurnEnd(entries: ReadonlyArray<SessionEntryLike>): TurnEnd;
@@ -78,10 +78,11 @@ export type SteerCtx = ExtensionCommandContext & {
78
78
  };
79
79
  /**
80
80
  * Timing knobs for the watchdog-abort guard in {@link steerUntilDone}, injectable
81
- * so tests exercise the grace expiry without a 10-second wait. `graceMs` bounds
82
- * how long the loop waits for the watchdog's follow-up to be DELIVERED (not to
83
- * finish its turn may legitimately run for minutes afterwards); delivery is
84
- * normally near-instant, so the grace only expires on a stale flag.
81
+ * so a test can exercise the grace expiry without waiting it out. `graceMs`
82
+ * bounds how long the loop waits for the watchdog's follow-up to be DELIVERED,
83
+ * not to finish: once it lands, the wait for its turn is unbounded. `onFire`
84
+ * sends the follow-up in the same block that raised the flag, so the grace
85
+ * expires only when the flag was already stale.
85
86
  */
86
87
  export interface SteerWatchdogDeps {
87
88
  consume: () => boolean;
@@ -95,6 +96,8 @@ export interface SteerWatchdogDeps {
95
96
  export interface ImplementationTurnDeps {
96
97
  /** The live session entries — the only thing the classifier reads. */
97
98
  entries: () => ReadonlyArray<SessionEntryLike>;
99
+ /** Test seam over the module-level one-shot; the real reader is the default. */
100
+ consumeGuardTermination?: () => boolean;
98
101
  /** Queue a follow-up user turn on the (idle) session. */
99
102
  send: (text: string) => Promise<void>;
100
103
  /** Wait for the session to go idle again. */
@@ -124,20 +127,23 @@ export declare function turnDepsFor(ctx: SteerCtx, opts?: SuperviseOptions): Imp
124
127
  /**
125
128
  * Nudge that resumes an implementation turn the runtime parked at a compaction
126
129
  * boundary. It must let a turn that was genuinely finished (then tipped over the
127
- * threshold by its own final message) confirm completion without inventing busywork
128
- * we cannot tell "paused mid-task by compaction" from "finished, then compacted"
129
- * from the boundary alone, so the wording lets a done turn end in one line.
130
+ * threshold by its own final message) confirm completion without inventing busywork.
131
+ * The classifier sees only the boundary's POSITION, so it cannot tell "paused
132
+ * mid-task by compaction" from "finished, then compacted"; the wording lets a done
133
+ * turn end in one line.
130
134
  */
131
135
  export declare const CONTINUE_AFTER_COMPACTION: string;
132
136
  /**
133
137
  * Safety cap on compaction-driven resumes for a single implementation turn. Each
134
- * resume follows a real compaction (which only fires after the model produced a
135
- * turn large enough to cross the threshold), so a legitimately large task may
136
- * resume a handful of times; the cap exists only to stop a pathological loop from
137
- * auto-sending forever with no user in the loop. Hitting it stops resuming and lets
138
- * the verify gate / `/task-auto-resume` catch any leftover incompleteness.
138
+ * resume follows a real compaction, which pi only runs once `shouldCompact` says
139
+ * the context crossed its threshold. The cap exists to stop a pathological loop
140
+ * from auto-sending forever with no user watching. Hitting it stops resuming and
141
+ * lets the verify gate and `/task-auto-resume` catch any leftover incompleteness.
139
142
  */
140
143
  export declare const MAX_COMPACTION_RESUMES = 20;
144
+ /** How a guard-stopped turn is reported. Named so a caller can tell it from a
145
+ * provider error: the fix is a different task, not a retry of this one. */
146
+ export declare const GUARD_TERMINATED = "the runaway guard stopped this turn: one tool call was repeated past every warning";
141
147
  /**
142
148
  * Resume an implementation turn that went idle at a threshold-compaction boundary.
143
149
  * The runtime compacts and parks at idle without auto-continuing; we send a
@@ -154,10 +160,12 @@ export declare function resumeAcrossCompactions(deps: ImplementationTurnDeps): P
154
160
  * `waitForIdle` resolves both on natural completion AND on an ESC (which aborts
155
161
  * the turn → idle). When the last turn was aborted, the host's main input loop is
156
162
  * blocked inside our command handler, so a message typed in the editor would only
157
- * queue, never run (interactive-mode routes idle input through onInputCallback,
158
- * which is unset while we hold the loop). We therefore solicit the steering text
159
- * ourselves and feed it back as another turn via sendUserMessage which runs to
160
- * completion when the session is idle. Repeat until a turn finishes uninterrupted.
163
+ * queue, never run: interactive-mode's submit handler calls `onInputCallback` when
164
+ * the session is idle, and that callback is set only inside `getUserInput()` the
165
+ * REPL loop we are holding so the text lands in `pendingUserInputs` instead. We
166
+ * therefore solicit the steering text ourselves and feed it back as another turn
167
+ * via `sendUserMessage`, which forwards to `prompt()` and, on an idle session, runs
168
+ * the turn rather than queueing it. Repeat until a turn finishes uninterrupted.
161
169
  *
162
170
  * A WATCHDOG abort also ends the turn with stopReason 'aborted' — indistinguishable
163
171
  * from a human ESC by the session entries alone at that instant. The watchdog
@@ -7,15 +7,17 @@
7
7
  * • `aborted` — a user ESC (or the command watchdog) cut the turn short;
8
8
  * • `compaction` — a threshold auto-compaction parked the turn at idle without
9
9
  * auto-continuing (the runtime expects a manual continue);
10
- * • `error` — the model/provider died mid-turn after pi's own retries;
10
+ * • `error` — the model or provider failed after pi exhausted the retries
11
+ * in its own retry settings;
11
12
  * • `stop` — genuine completion.
12
- * `classifyTurnEnd` reads the session entries and names ONE of those, in the
13
- * precedence the supervision sequence needs; `superviseImplementation` then
14
- * resumes across compactions, lets the user steer after an interrupt, and reports
15
- * the terminal outcome. The orchestrator calls it once.
13
+ * `classifyTurnEnd` reads the session entries and names ONE of those;
14
+ * `superviseImplementation` then resumes across compactions, lets the user steer
15
+ * after an interrupt, and reports the terminal outcome. The orchestrator calls it
16
+ * from one place, inside the `sendSpec` closure.
16
17
  */
17
18
  import { SessionUI } from '../remote/bridge.js';
18
19
  import { consumeWatchdogAbort, WATCHDOG_CANCEL_MARKER } from './command-watchdog.js';
20
+ import { consumeGuardTermination } from './implementation-guards.js';
19
21
  const isAssistant = (e) => e.message !== undefined && e.message.role === 'assistant';
20
22
  /** Index of the last assistant message and of the last compaction boundary. */
21
23
  function tailPositions(entries) {
@@ -33,21 +35,20 @@ function tailPositions(entries) {
33
35
  /**
34
36
  * Classify how the most recent turn ended, from the session entries alone.
35
37
  *
36
- * Precedence, when several signals are present at once (this is the order the
37
- * supervision sequence has always applied, now stated in one place):
38
+ * Precedence, when several signals are present at once:
38
39
  * 1. `aborted` — the last assistant message has stopReason "aborted". A user
39
40
  * ESC (or watchdog abort) wins over everything: it is not a
40
41
  * compaction pause, and the steer loop owns it.
41
42
  * 2. `compaction` — a `compaction` entry sits AFTER the last assistant message.
42
- * Position-based, not timestamp-based: the runtime appends the
43
- * boundary to the tail of the branch after the message that
44
- * triggered it (`appendCompaction` `_appendEntry` push), so a
43
+ * Position-based, not timestamp-based: `appendCompaction`
44
+ * pushes the boundary onto the tail of the entry list, and
45
+ * `getEntries()` returns that list in append order, so a
45
46
  * trailing compaction means we are parked with no continuation.
46
- * A finished turn ends on an assistant message; an *overflow*
47
- * compaction self-retries and never leaves us idle here.
48
- * 3. `error` — the last assistant message has stopReason "error": the
49
- * model/provider died (context-overflow 400, disconnect, 5xx)
50
- * after pi exhausted its own retries.
47
+ * A finished turn ends on an assistant message. An overflow
48
+ * compaction that is going to retry continues the turn itself
49
+ * and never reaches us idle.
50
+ * 3. `error` — the last assistant message has stopReason "error": the model
51
+ * or provider failed after pi exhausted its own retries.
51
52
  * 4. `stop` — anything else, including a session with no assistant turn.
52
53
  */
53
54
  export function classifyTurnEnd(entries) {
@@ -143,9 +144,10 @@ export function turnDepsFor(ctx, opts = {}) {
143
144
  /**
144
145
  * Nudge that resumes an implementation turn the runtime parked at a compaction
145
146
  * boundary. It must let a turn that was genuinely finished (then tipped over the
146
- * threshold by its own final message) confirm completion without inventing busywork
147
- * we cannot tell "paused mid-task by compaction" from "finished, then compacted"
148
- * from the boundary alone, so the wording lets a done turn end in one line.
147
+ * threshold by its own final message) confirm completion without inventing busywork.
148
+ * The classifier sees only the boundary's POSITION, so it cannot tell "paused
149
+ * mid-task by compaction" from "finished, then compacted"; the wording lets a done
150
+ * turn end in one line.
149
151
  */
150
152
  export const CONTINUE_AFTER_COMPACTION = 'Your context was automatically compacted. Continue implementing this task from '
151
153
  + 'exactly where you left off, and keep going until it is fully done. If the '
@@ -153,13 +155,15 @@ export const CONTINUE_AFTER_COMPACTION = 'Your context was automatically compact
153
155
  + 'extra work or restart the task.';
154
156
  /**
155
157
  * Safety cap on compaction-driven resumes for a single implementation turn. Each
156
- * resume follows a real compaction (which only fires after the model produced a
157
- * turn large enough to cross the threshold), so a legitimately large task may
158
- * resume a handful of times; the cap exists only to stop a pathological loop from
159
- * auto-sending forever with no user in the loop. Hitting it stops resuming and lets
160
- * the verify gate / `/task-auto-resume` catch any leftover incompleteness.
158
+ * resume follows a real compaction, which pi only runs once `shouldCompact` says
159
+ * the context crossed its threshold. The cap exists to stop a pathological loop
160
+ * from auto-sending forever with no user watching. Hitting it stops resuming and
161
+ * lets the verify gate and `/task-auto-resume` catch any leftover incompleteness.
161
162
  */
162
163
  export const MAX_COMPACTION_RESUMES = 20;
164
+ /** How a guard-stopped turn is reported. Named so a caller can tell it from a
165
+ * provider error: the fix is a different task, not a retry of this one. */
166
+ export const GUARD_TERMINATED = 'the runaway guard stopped this turn: one tool call was repeated past every warning';
163
167
  /**
164
168
  * Resume an implementation turn that went idle at a threshold-compaction boundary.
165
169
  * The runtime compacts and parks at idle without auto-continuing; we send a
@@ -209,10 +213,12 @@ async function awaitWatchdogFollowUp(deps) {
209
213
  * `waitForIdle` resolves both on natural completion AND on an ESC (which aborts
210
214
  * the turn → idle). When the last turn was aborted, the host's main input loop is
211
215
  * blocked inside our command handler, so a message typed in the editor would only
212
- * queue, never run (interactive-mode routes idle input through onInputCallback,
213
- * which is unset while we hold the loop). We therefore solicit the steering text
214
- * ourselves and feed it back as another turn via sendUserMessage which runs to
215
- * completion when the session is idle. Repeat until a turn finishes uninterrupted.
216
+ * queue, never run: interactive-mode's submit handler calls `onInputCallback` when
217
+ * the session is idle, and that callback is set only inside `getUserInput()` the
218
+ * REPL loop we are holding so the text lands in `pendingUserInputs` instead. We
219
+ * therefore solicit the steering text ourselves and feed it back as another turn
220
+ * via `sendUserMessage`, which forwards to `prompt()` and, on an idle session, runs
221
+ * the turn rather than queueing it. Repeat until a turn finishes uninterrupted.
216
222
  *
217
223
  * A WATCHDOG abort also ends the turn with stopReason 'aborted' — indistinguishable
218
224
  * from a human ESC by the session entries alone at that instant. The watchdog
@@ -258,6 +264,13 @@ export async function superviseWith(deps) {
258
264
  const interrupted = await steerUntilDone(deps);
259
265
  // A user-declined steer (interrupted) is its own paused path; otherwise
260
266
  // inspect how the turn actually ended.
261
- const error = interrupted ? undefined : turnErrorMessage(deps.entries());
267
+ // The runaway guard ends a turn WITHOUT an error stopReason, so classifyTurnEnd
268
+ // reads `'stop'` and the caller would verify a half-done implementation and
269
+ // re-deliver to a model that deterministically re-thrashes. Consumed here
270
+ // because this is the one place that reports how the turn really ended.
271
+ const guardEnded = deps.consumeGuardTermination?.() ?? consumeGuardTermination();
272
+ const error = interrupted ? undefined
273
+ : guardEnded ? GUARD_TERMINATED
274
+ : turnErrorMessage(deps.entries());
262
275
  return { interrupted, error, resumes };
263
276
  }
@@ -1,13 +1,26 @@
1
1
  /**
2
- * Inline-markdown helpers for the clarify/grill question dialogs.
2
+ * Inline-markdown helpers for the question dialogs (grill, clarify, /task-plan).
3
3
  *
4
- * The model often wraps the core question in **bold** (and code in backticks)
5
- * because it makes the question easier to read at a glance. ctx.ui.input titles
6
- * accept ANSI styling, so we RENDER those spans to terminal bold/code for the
7
- * displayed prompt, and STRIP them to plain text for the editable input default
8
- * and the persisted task file (which must stay ANSI-free).
4
+ * The question arrives carrying markdown because our own prompts ask for it:
5
+ * auto-prompts.ts and plan-prompts.ts both say "Put the core question in
6
+ * **bold** ... Backticks around code/identifiers are fine."
7
+ *
8
+ * One question then needs two forms, and question-dialog.ts `settleQuestion`
9
+ * builds both:
10
+ * • RENDERED — passed as `localTitle`, which reaches `ctx.ui.input`. pi wraps
11
+ * that title in `theme.fg("accent", ...)` and hands it to a pi-tui `Text`,
12
+ * which passes embedded escape sequences straight through, so the bold and
13
+ * code spans survive to the terminal.
14
+ * • STRIPPED — passed as the browser card's `question`, as the editable
15
+ * `recommended` default, and as the text recorded in QaTranscript, whose
16
+ * `forRecord()` is written into the task file's `grill Q&A` section. All
17
+ * three are plain text, never ANSI.
18
+ */
19
+ /**
20
+ * Minimal theme surface we need. pi's `Theme` satisfies it: it declares
21
+ * `bold(text)` and `fg(color, text)`, and `mdCode` is one of its `ThemeColor`
22
+ * values, so `ctx.ui.theme` is passed in directly.
9
23
  */
10
- /** Minimal theme surface we need; ExtensionCommandContext['ui'].theme satisfies it. */
11
24
  export interface InlineMarkdownTheme {
12
25
  bold(text: string): string;
13
26
  fg(color: 'mdCode', text: string): string;
@@ -1,11 +1,20 @@
1
1
  /**
2
- * Inline-markdown helpers for the clarify/grill question dialogs.
2
+ * Inline-markdown helpers for the question dialogs (grill, clarify, /task-plan).
3
3
  *
4
- * The model often wraps the core question in **bold** (and code in backticks)
5
- * because it makes the question easier to read at a glance. ctx.ui.input titles
6
- * accept ANSI styling, so we RENDER those spans to terminal bold/code for the
7
- * displayed prompt, and STRIP them to plain text for the editable input default
8
- * and the persisted task file (which must stay ANSI-free).
4
+ * The question arrives carrying markdown because our own prompts ask for it:
5
+ * auto-prompts.ts and plan-prompts.ts both say "Put the core question in
6
+ * **bold** ... Backticks around code/identifiers are fine."
7
+ *
8
+ * One question then needs two forms, and question-dialog.ts `settleQuestion`
9
+ * builds both:
10
+ * • RENDERED — passed as `localTitle`, which reaches `ctx.ui.input`. pi wraps
11
+ * that title in `theme.fg("accent", ...)` and hands it to a pi-tui `Text`,
12
+ * which passes embedded escape sequences straight through, so the bold and
13
+ * code spans survive to the terminal.
14
+ * • STRIPPED — passed as the browser card's `question`, as the editable
15
+ * `recommended` default, and as the text recorded in QaTranscript, whose
16
+ * `forRecord()` is written into the task file's `grill Q&A` section. All
17
+ * three are plain text, never ANSI.
9
18
  */
10
19
  const BOLD_SPAN = /\*\*(.+?)\*\*/g;
11
20
  const CODE_SPAN = /`([^`]+)`/g;
@@ -1,65 +1,51 @@
1
1
  /**
2
2
  * launch-config-gap — a launch script that cannot run because a variable the
3
3
  * project's own tracked template DECLARES is absent from this box is an
4
- * ENVIRONMENT GAP, not a code fault (mx5 run 20).
4
+ * ENVIRONMENT GAP, not a code fault.
5
5
  *
6
- * THE EPISODE. Attempt 3 of the final-gate autofix passed every guard, fixed the
7
- * failing test, and still lost the run:
8
- *
9
- * autofix attempt 3 failed did not converge: launch script: `bun run seed`
10
- * exited 1 $ bun run src/server/seed.ts Missing required environment
11
- * variable: ADMIN_PHONE error: script "seed" exited with code 1
12
- *
13
- * Single-failure phrasing (final-gate.ts uses a numbered list for ≥2), so the
14
- * suite was green and this was the only remaining failure. The run failed on
15
- * exactly this, and it is self-inflicted at the last moment: pre-gate
16
- * `package.json` had no `seed` script at all, so `if (!present.has(name)) continue`
17
- * skipped it. Attempt 3 added the script to clear the launch-contract diff — and
18
- * thereby armed the check that killed the run.
19
- *
20
- * The loop was closed by construction. The gate runs every declared non-boot
21
- * script with `runnerEnv(runner)` = `process.env` plus a PATH prefix and nothing
22
- * else; "Missing required environment variable" matches neither ENV_GAP_OUTPUT_RE
23
- * nor INFRA_GAP_OUTPUT_RE, so it is a hard FAIL; and the only way to supply the
24
- * value is a gitignored `.env`, whose writes nexttask 4 correctly refuses to
25
- * credit. The static half of this already shipped and WORKED — `.env.example`
26
- * declares ADMIN_PHONE/ADMIN_PASSWORD, so `findMissingEnvDeclarations` was
27
- * correctly silent. This is the execution half.
6
+ * WHY THE GATE CANNOT SETTLE THIS ON ITS OWN. final-gate.ts runs every declared
7
+ * non-boot script (`runnableDeclaredScripts`, which filters on `BOOT_CLASS_RE`)
8
+ * as `bun run <name>`, with `runnerEnv(runner)` — `process.env` plus at most a
9
+ * PATH prefix, and nothing else. A project's own "missing environment variable"
10
+ * message matches neither `ENV_GAP_OUTPUT_RE` nor `INFRA_GAP_OUTPUT_RE`, so it
11
+ * lands as a hard FAIL. The only way to supply the value is a gitignored `.env`,
12
+ * and final-gate-fix.ts downgrades a converged PASS to UNOBSERVED when the gate
13
+ * stops passing with the ignored paths moved aside. Without this check the loop
14
+ * has no exit.
28
15
  *
29
16
  * FOUR STATIC CONDITIONS, ALL REQUIRED. Deliberately over-constrained: the
30
17
  * failure mode of getting this wrong is a gate that excuses real breakage.
31
18
  *
32
- * 1. the script's resolved body names a TRACKED SOURCE FILE
33
- * (`bun run src/server/seed.ts` → `src/server/seed.ts`);
19
+ * 1. the script's resolved body names a TRACKED SOURCE FILE;
34
20
  * 2. that file REQUIRES an env var `X` under `scanSource` — i.e. none of its
35
- * step-asides (default / compared / assigned / ambient / …) applies;
21
+ * step-asides (`default`, `compared`, `assigned`, `ambient`,
22
+ * `optional-api`, `probe`) applies;
36
23
  * 3. `X` is DECLARED in the tracked template. If it is NOT,
37
24
  * `findMissingEnvDeclarations` has already failed the gate statically and
38
25
  * this path must not fire — otherwise the two checks would cancel out and a
39
26
  * project with no template at all would gain a blanket excuse;
40
27
  * 4. `X` is ABSENT from the env the gate spawned the child with.
41
28
  *
42
- * NOTHING IS PARSED FROM THE CHILD'S STDERR. `Missing required environment
43
- * variable: ADMIN_PHONE` is a string the PROJECT authored; matching on it would
44
- * be a rule about one project's phrasing, and every other project would phrase it
45
- * differently or not at all.
29
+ * NOTHING IS PARSED FROM THE CHILD'S STDERR. A "missing variable" message is a
30
+ * string the PROJECT authored; matching on it would be a rule about one project's
31
+ * phrasing, and the next project phrases it differently or not at all.
46
32
  *
47
33
  * THE FIFTH CONDITION IS DYNAMIC, AND IT IS WHAT MAKES THE RULE HONEST. The four
48
34
  * above cannot tell "exited BECAUSE the variable is absent" from "exited for its
49
35
  * own reasons, and also happens to read an absent variable". A script that throws
50
- * a TypeError on line 1 and also reads ADMIN_PHONE on line 3 satisfies all four.
36
+ * a TypeError on line 1 and also reads the variable on line 3 satisfies all four.
51
37
  * So the script is re-run once with the gap variables supplied as OBVIOUSLY
52
38
  * SYNTHETIC placeholders, and the exit code decides:
53
39
  *
54
- * still non-zero the absence did not cause it FAIL, as today
55
- * now zero the absence did cause it skip + UNOBSERVED + debt
40
+ * still non-zero -> the absence did not cause it -> FAIL, unchanged
41
+ * now zero -> the absence did cause it -> skip + UNOBSERVED + debt
56
42
  *
57
- * The probe run is a DIAGNOSTIC, never an observation. Its success is not
58
- * reported, and the verdict it produces is UNOBSERVED with debt — never a PASS
59
- * (memory/unobserved-gate-verdict-shipped.md). The placeholder is a fixed
60
- * harness-authored string; `.env.example`'s own values are never injected,
61
- * because they are placeholders too (`change-me`) and a green seed run against
62
- * them would be a fabricated observation.
43
+ * The probe run is a DIAGNOSTIC, never an observation. On a passing probe
44
+ * final-gate.ts calls `tally.unobserve()` and files the script as skipped, so the
45
+ * verdict is UNOBSERVED with debt and never a PASS. The placeholder is one fixed
46
+ * harness-authored string ({@link CONFIG_GAP_PROBE_VALUE}); the template's own
47
+ * values are never injected, because those are placeholders too and a green run
48
+ * against them would be a fabricated observation.
63
49
  */
64
50
  import { readFileSync } from 'node:fs';
65
51
  import * as path from 'node:path';
@@ -12,22 +12,20 @@ export declare function parseScriptLines(text: string): string[];
12
12
  */
13
13
  export declare function keepGroundedScripts(names: string[], sourceDoc: string): string[];
14
14
  /**
15
- * DETERMINISTIC RECALL (mx5 run 11): enumerate every backticked, script-name-shaped
15
+ * DETERMINISTIC RECALL: enumerate every backticked, script-name-shaped
16
16
  * token in a paragraph that mentions the word "script", as extraction CANDIDATES.
17
17
  *
18
- * The run-11 failure this closes: `test:ct` is backticked in the design's §2 tooling
19
- * paragraph, so the grounding guard would have KEPT it but the extraction child
20
- * anchored on §9's one-line summary (`dev`,`build`,`migrate`,`seed`,`test`) and never
21
- * emitted it. Grounding can only DROP a candidate, never add one, so recall was
22
- * entirely the model's, over a 20KB doc. This makes recall mechanical: the host
23
- * enumerates candidates and hands them to the child as an explicit checklist; the
24
- * model's job flips from recall (weak) to per-candidate classification (strong).
18
+ * Grounding can only DROP a candidate, never add one, so without this the recall of a
19
+ * script declared far from the design's summary list is entirely the model's. This
20
+ * makes recall mechanical: the host enumerates the candidates and hands them to the
21
+ * child as an explicit checklist, so the model's job flips from recall (weak) to
22
+ * per-candidate classification (strong).
25
23
  *
26
24
  * The paragraph gate (`\bscripts?\b`, word-bounded so "TypeScript"/"JavaScript"
27
25
  * don't match) is a grounded-context filter, not a tuned knob: a design declares a
28
- * script by calling it one. It keeps package-name paragraphs (`hono`, `react`) out
29
- * of the checklist so a weak model isn't invited to keep junk the grounding guard
30
- * would then bless (every package name is backticked somewhere). A design with no
26
+ * script by calling it one. It keeps package-name paragraphs out of the checklist so
27
+ * a weak model isn't invited to keep junk the grounding guard would then bless —
28
+ * every package name is backticked somewhere. A design with no
31
29
  * such paragraph yields no candidates and the prompt is unchanged.
32
30
  */
33
31
  export declare function enumerateScriptCandidates(sourceDoc: string): string[];
@@ -38,13 +36,12 @@ export declare function readDeclaredScripts(cwd: string): Promise<string[]>;
38
36
  /** Append grounded script names, deduped against what is stored, keeping newest MAX. */
39
37
  export declare function appendDeclaredScripts(cwd: string, names: string[]): Promise<void>;
40
38
  /**
41
- * The declared scripts the final gate must EXECUTE as one-shot commands (mx5 run
42
- * 11): everything the launch contract declares that is neither boot-class (the
43
- * boot check exercises those) nor already covered by the gate's integration
44
- * commands (`covered`, case-insensitive — the test/build-shaped scripts that ran).
45
- * Run 11 shipped `migrate` and `seed` broken (`.rows` on a Bun sql array —
46
- * TypeError on first call) while the gate checked only that the scripts EXIST;
47
- * existence is not launchability.
39
+ * The declared scripts the final gate must EXECUTE as one-shot commands: everything
40
+ * the launch contract declares that is neither boot-class (the boot check exercises
41
+ * those) nor already covered by the gate's integration commands (`covered`,
42
+ * case-insensitive — the test/build-shaped scripts that ran). Checking only that a
43
+ * script is DECLARED says nothing about whether running it works; existence is not
44
+ * launchability.
48
45
  */
49
46
  export declare function runnableDeclaredScripts(declared: string[], covered: string[]): string[];
50
47
  /**
@@ -59,8 +56,8 @@ export declare function missingDeclaredScripts(declared: string[], manifestScrip
59
56
  * (keepGroundedScripts), so a hallucinated script cannot reach the diff.
60
57
  *
61
58
  * `candidates` is enumerateScriptCandidates' mechanical checklist. It exists so the
62
- * model cannot MISS a declared script buried far from the design's summary list (the
63
- * run-11 `test:ct` hole); the model still classifies each candidate against the
64
- * design, and the host grounding still applies. Empty ⇒ the prompt is unchanged.
59
+ * model cannot MISS a declared script buried far from the design's summary list; the
60
+ * model still classifies each candidate against the design, and the host grounding
61
+ * still applies. Empty ⇒ the prompt is unchanged.
65
62
  */
66
63
  export declare const LAUNCH_EXTRACT_PROMPT: (feature: string, candidates?: string[]) => string;