@mjasnikovs/pi-task 0.38.29 → 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 +16 -9
  104. package/dist/task/context-usage.js +16 -9
  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 +36 -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 +179 -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 +29 -39
  231. package/dist/task/research-worker.js +37 -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 +69 -71
  338. package/dist/workers/pi-worker-core.js +100 -109
  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 +21 -14
  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
@@ -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 { SessionUI } from '../remote/bridge.js';
18
19
  import { consumeWatchdogAbort, WATCHDOG_CANCEL_MARKER } from './command-watchdog.js';
@@ -33,21 +34,20 @@ function tailPositions(entries) {
33
34
  /**
34
35
  * Classify how the most recent turn ended, from the session entries alone.
35
36
  *
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):
37
+ * Precedence, when several signals are present at once:
38
38
  * 1. `aborted` — the last assistant message has stopReason "aborted". A user
39
39
  * ESC (or watchdog abort) wins over everything: it is not a
40
40
  * compaction pause, and the steer loop owns it.
41
41
  * 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
42
+ * Position-based, not timestamp-based: `appendCompaction`
43
+ * pushes the boundary onto the tail of the entry list, and
44
+ * `getEntries()` returns that list in append order, so a
45
45
  * 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.
46
+ * A finished turn ends on an assistant message. An overflow
47
+ * compaction that is going to retry continues the turn itself
48
+ * and never reaches us idle.
49
+ * 3. `error` — the last assistant message has stopReason "error": the model
50
+ * or provider failed after pi exhausted its own retries.
51
51
  * 4. `stop` — anything else, including a session with no assistant turn.
52
52
  */
53
53
  export function classifyTurnEnd(entries) {
@@ -143,9 +143,10 @@ export function turnDepsFor(ctx, opts = {}) {
143
143
  /**
144
144
  * Nudge that resumes an implementation turn the runtime parked at a compaction
145
145
  * 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.
146
+ * threshold by its own final message) confirm completion without inventing busywork.
147
+ * The classifier sees only the boundary's POSITION, so it cannot tell "paused
148
+ * mid-task by compaction" from "finished, then compacted"; the wording lets a done
149
+ * turn end in one line.
149
150
  */
150
151
  export const CONTINUE_AFTER_COMPACTION = 'Your context was automatically compacted. Continue implementing this task from '
151
152
  + 'exactly where you left off, and keep going until it is fully done. If the '
@@ -153,11 +154,10 @@ export const CONTINUE_AFTER_COMPACTION = 'Your context was automatically compact
153
154
  + 'extra work or restart the task.';
154
155
  /**
155
156
  * 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.
157
+ * resume follows a real compaction, which pi only runs once `shouldCompact` says
158
+ * the context crossed its threshold. The cap exists to stop a pathological loop
159
+ * from auto-sending forever with no user watching. Hitting it stops resuming and
160
+ * lets the verify gate and `/task-auto-resume` catch any leftover incompleteness.
161
161
  */
162
162
  export const MAX_COMPACTION_RESUMES = 20;
163
163
  /**
@@ -209,10 +209,12 @@ async function awaitWatchdogFollowUp(deps) {
209
209
  * `waitForIdle` resolves both on natural completion AND on an ESC (which aborts
210
210
  * the turn → idle). When the last turn was aborted, the host's main input loop is
211
211
  * 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.
212
+ * queue, never run: interactive-mode's submit handler calls `onInputCallback` when
213
+ * the session is idle, and that callback is set only inside `getUserInput()` the
214
+ * REPL loop we are holding so the text lands in `pendingUserInputs` instead. We
215
+ * therefore solicit the steering text ourselves and feed it back as another turn
216
+ * via `sendUserMessage`, which forwards to `prompt()` and, on an idle session, runs
217
+ * the turn rather than queueing it. Repeat until a turn finishes uninterrupted.
216
218
  *
217
219
  * A WATCHDOG abort also ends the turn with stopReason 'aborted' — indistinguishable
218
220
  * from a human ESC by the session entries alone at that instant. The watchdog
@@ -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;
@@ -3,13 +3,10 @@
3
3
  * declares the finished project must expose, extracted once at plan time and diffed
4
4
  * by the final gate against the shipped manifest.
5
5
  *
6
- * The failure this closes (mx5 run 10 item 4): the design's §9 "Build & run" listed
7
- * the required scripts verbatim `dev`, `build`, `migrate`, `seed`, `test` but the
8
- * shipped package.json declared only `dev`, `build`, `lint`, `test`, `test:ct`. No
9
- * task owned `migrate`/`seed` (they fell through decompose), and NOTHING re-checked
10
- * the finished manifest against the design's own list, so the run completed missing
11
- * two of its declared entrypoints. A per-slice gate cannot catch this — it is a
12
- * whole-project launch-surface fact — and the final gate never had the design's list.
6
+ * The gap it closes is structural. A script the design names but no task owned can
7
+ * fall through decompose and ship missing, and a per-slice gate cannot see that: it
8
+ * is a whole-project launch-surface fact, and nothing else re-reads the finished
9
+ * manifest against the design's own list.
13
10
  *
14
11
  * Mechanism (mirrors contracts.ts): a plan-time child EMITs `SCRIPT:` lines naming the
15
12
  * scripts the design declares; the host GROUNDS each against the design — a name is
@@ -17,9 +14,13 @@
17
14
  * form designs use to declare a script. A paraphrase or a script the model invented is
18
15
  * not grounded and is dropped, so the diff can never false-flag on a hallucinated
19
16
  * requirement. The grounded list is appended HOST-SIDE to `.pi-tasks/launch-contract.md`
20
- * (children never write it), which survives discardEdits and the git-state guard.
17
+ * the extraction child runs `--no-tools` and so has no write tool at all. Both halves
18
+ * of `discardTreeEdits` skip that directory (`checkout -- . :(exclude).pi-tasks`, then
19
+ * `clean -fd -e .pi-tasks`), and the git-state guard excludes it from the tree it
20
+ * hashes, so the file outlives a discarded pass.
21
21
  *
22
- * At run end the final gate reads the list, reads the manifest's `scripts`, and FAILs
22
+ * At run end the final gate reads the list, reads the shipped manifest's script names
23
+ * (`readLaunchManifest`: package.json `scripts`, else a Makefile's targets), and FAILs
23
24
  * naming any declared script the manifest is missing. FP-safe by construction: an
24
25
  * empty/ungrounded list (a design that never backticks a script name) yields no check.
25
26
  */
@@ -93,22 +94,20 @@ export function keepGroundedScripts(names, sourceDoc) {
93
94
  return kept;
94
95
  }
95
96
  /**
96
- * DETERMINISTIC RECALL (mx5 run 11): enumerate every backticked, script-name-shaped
97
+ * DETERMINISTIC RECALL: enumerate every backticked, script-name-shaped
97
98
  * token in a paragraph that mentions the word "script", as extraction CANDIDATES.
98
99
  *
99
- * The run-11 failure this closes: `test:ct` is backticked in the design's §2 tooling
100
- * paragraph, so the grounding guard would have KEPT it but the extraction child
101
- * anchored on §9's one-line summary (`dev`,`build`,`migrate`,`seed`,`test`) and never
102
- * emitted it. Grounding can only DROP a candidate, never add one, so recall was
103
- * entirely the model's, over a 20KB doc. This makes recall mechanical: the host
104
- * enumerates candidates and hands them to the child as an explicit checklist; the
105
- * model's job flips from recall (weak) to per-candidate classification (strong).
100
+ * Grounding can only DROP a candidate, never add one, so without this the recall of a
101
+ * script declared far from the design's summary list is entirely the model's. This
102
+ * makes recall mechanical: the host enumerates the candidates and hands them to the
103
+ * child as an explicit checklist, so the model's job flips from recall (weak) to
104
+ * per-candidate classification (strong).
106
105
  *
107
106
  * The paragraph gate (`\bscripts?\b`, word-bounded so "TypeScript"/"JavaScript"
108
107
  * don't match) is a grounded-context filter, not a tuned knob: a design declares a
109
- * script by calling it one. It keeps package-name paragraphs (`hono`, `react`) out
110
- * of the checklist so a weak model isn't invited to keep junk the grounding guard
111
- * would then bless (every package name is backticked somewhere). A design with no
108
+ * script by calling it one. It keeps package-name paragraphs out of the checklist so
109
+ * a weak model isn't invited to keep junk the grounding guard would then bless —
110
+ * every package name is backticked somewhere. A design with no
112
111
  * such paragraph yields no candidates and the prompt is unchanged.
113
112
  */
114
113
  export function enumerateScriptCandidates(sourceDoc) {
@@ -153,13 +152,12 @@ export async function appendDeclaredScripts(cwd, names) {
153
152
  */
154
153
  const BOOT_CLASS_RE = /^(?:dev|start|serve|preview|watch)(?:[:_-].*)?$/i;
155
154
  /**
156
- * The declared scripts the final gate must EXECUTE as one-shot commands (mx5 run
157
- * 11): everything the launch contract declares that is neither boot-class (the
158
- * boot check exercises those) nor already covered by the gate's integration
159
- * commands (`covered`, case-insensitive — the test/build-shaped scripts that ran).
160
- * Run 11 shipped `migrate` and `seed` broken (`.rows` on a Bun sql array —
161
- * TypeError on first call) while the gate checked only that the scripts EXIST;
162
- * existence is not launchability.
155
+ * The declared scripts the final gate must EXECUTE as one-shot commands: everything
156
+ * the launch contract declares that is neither boot-class (the boot check exercises
157
+ * those) nor already covered by the gate's integration commands (`covered`,
158
+ * case-insensitive — the test/build-shaped scripts that ran). Checking only that a
159
+ * script is DECLARED says nothing about whether running it works; existence is not
160
+ * launchability.
163
161
  */
164
162
  export function runnableDeclaredScripts(declared, covered) {
165
163
  const have = new Set(covered.map(s => s.toLowerCase()));
@@ -189,9 +187,9 @@ export function missingDeclaredScripts(declared, manifestScripts) {
189
187
  * (keepGroundedScripts), so a hallucinated script cannot reach the diff.
190
188
  *
191
189
  * `candidates` is enumerateScriptCandidates' mechanical checklist. It exists so the
192
- * model cannot MISS a declared script buried far from the design's summary list (the
193
- * run-11 `test:ct` hole); the model still classifies each candidate against the
194
- * design, and the host grounding still applies. Empty ⇒ the prompt is unchanged.
190
+ * model cannot MISS a declared script buried far from the design's summary list; the
191
+ * model still classifies each candidate against the design, and the host grounding
192
+ * still applies. Empty ⇒ the prompt is unchanged.
195
193
  */
196
194
  export const LAUNCH_EXTRACT_PROMPT = (feature, candidates = []) => [
197
195
  'You are recording the PACKAGE/BUILD SCRIPTS the design below says the finished',
@@ -31,12 +31,16 @@ export declare function makeTargets(src: string): string[];
31
31
  export declare function readLaunchManifest(cwd: string): LaunchManifest;
32
32
  /**
33
33
  * The one line an INERT contract leaves behind. It rides in the gate's UNOBSERVED
34
- * channel rather than in `warnings` for the reason nexttask 16A gives: a check that
34
+ * channel rather than in `warnings` for one reason: a check that
35
35
  * silently did nothing must not read later as a check that passed.
36
36
  */
37
37
  export declare function inertLaunchContractNote(declared: string[], manifest: LaunchManifest): string;
38
38
  /** The `scripts` map of this tree's package.json, or `{}` when there isn't one.
39
39
  * Shared by the gate and the boot probe — both ask the same manifest question. */
40
40
  export declare function packageScripts(cwd: string): Record<string, string>;
41
- /** Does this tree's Makefile declare `target`? */
41
+ /**
42
+ * Does this tree's Makefile declare `target`? Reads the literal name `Makefile`
43
+ * only — unlike {@link readLaunchManifest}, this one does not walk make's lookup
44
+ * order, so a `GNUmakefile`-only tree answers false.
45
+ */
42
46
  export declare function makeHasTarget(cwd: string, target: string): boolean;
@@ -1,54 +1,50 @@
1
1
  /**
2
2
  * launch-manifest — WHICH manifest the launch-contract diff is entitled to diff
3
- * against, and whether there is one at all (nexttask 16A).
3
+ * against, and whether there is one at all.
4
4
  *
5
- * The defect this closes (unobserved, proven by construction). The extraction end
6
- * of the launch contract has no ecosystem test anywhere in it:
7
- * `enumerateScriptCandidates` scrapes backticked, script-name-shaped tokens out of
8
- * any design paragraph that says "script", and `keepGroundedScripts` can only DROP
9
- * a candidate. So a CMake / cargo / poetry / Makefile design saying *"the Makefile
10
- * must expose `build`, `test`, `migrate`"* records exactly the same artifact an npm
11
- * design does. The diff end hardcoded the ecosystem anyway:
5
+ * WHY THE DIFF NEEDS ITS OWN RESOLVER. The extraction end of the launch contract
6
+ * has no ecosystem test anywhere in it: `enumerateScriptCandidates` scrapes
7
+ * backticked, script-name-shaped tokens out of any design paragraph that says
8
+ * "script", and `keepGroundedScripts` can only DROP a candidate. A Makefile design
9
+ * saying *"the Makefile must expose `build`, `test`, `migrate`"* therefore records
10
+ * exactly the same artifact an npm design does.
12
11
  *
13
- * missingDeclaredScripts(declared, Object.keys(packageScripts(cwd)))
14
- * …where packageScripts' catch returns {} so "this project has no
15
- * package.json" was indistinguishable from "its package.json declares no
16
- * scripts", and every declared script was reported missing, at rank 1, in
17
- * wording that NAMES a file the project was never meant to have.
18
- *
19
- * That text seeds the autofix child's prompt (final-gate.ts's FinalGateOutcome.reason),
20
- * so the most likely repair on a CMake project was to write a package.json.
21
- *
22
- * IAR1 is how close this got: `plan-debug.log:10` records "launch-contract
23
- * extraction: 0 grounded script(s) kept from 3 emitted" — a non-npm project that
24
- * reached extraction and emitted three candidates, saved only by its design not
25
- * backticking them in a "script" paragraph.
12
+ * The diff cannot then reach for `packageScripts` alone. Its catch returns `{}`, so
13
+ * "this project has no package.json" is indistinguishable from "its package.json
14
+ * declares no scripts", and every declared script reads as missing — in wording that
15
+ * NAMES a file the project was never meant to have. `tally.fail` text becomes the
16
+ * gate's `reason`, which `buildFinalFixPrompt` hands to the autofix child, so on a
17
+ * non-npm tree the repair that text invites is to write a package.json.
26
18
  *
27
19
  * THE RULE, and it is deliberately two ecosystems wide, not three:
28
20
  * • package.json present and parseable ⇒ diff against its `scripts` keys, exactly
29
- * as before (byte-identical failure text on every npm tree — mx5's missing
21
+ * as before (byte-identical failure text on every npm tree — a missing
30
22
  * `build`/`seed` is a TRUE positive and must keep failing).
31
- * • no package.json but a Makefile ⇒ diff against its TARGETS. The capability was
32
- * already in final-gate.ts (`makeHasTarget`); the diff simply never called it.
23
+ * • no package.json but a Makefile ⇒ diff against its TARGETS.
33
24
  * • neither ⇒ the check is INERT. No failure, and a note saying the contract was
34
25
  * recorded but is not checkable here, so the silence is not read later as a pass
35
26
  * (the repo-health-check / env-template-closure discipline: ENOENT = pass, and a
36
27
  * check must never invent a file the project chose not to have).
37
28
  *
38
- * cargo/poetry/gradle are NOT here on purpose. The corpus on this box contains one
39
- * project that ever recorded a launch contract (mx5, npm) and one non-npm project
40
- * that reached extraction (IAR1, CMake). A third ecosystem would be a guess.
29
+ * cargo/poetry/gradle are NOT here on purpose: nothing in this repo knows how to
30
+ * enumerate their entrypoints, and a third resolver written without one would be a
31
+ * guess that can only produce false FAILs.
41
32
  */
42
33
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
43
34
  import * as path from 'node:path';
44
- /** GNU make's own lookup order, and nothing beyond it — this is one ecosystem. */
35
+ /**
36
+ * GNU make's own lookup order, and nothing beyond it — this is one ecosystem.
37
+ * Confirmed against `make` itself: with all three files present in one directory it
38
+ * runs GNUmakefile; with only `makefile` and `Makefile`, it runs `makefile`.
39
+ */
45
40
  const MAKEFILE_NAMES = ['GNUmakefile', 'makefile', 'Makefile'];
46
41
  /** A target we are willing to name in a failure: an ordinary word-shaped target. */
47
42
  const TARGET_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,39}$/i;
48
43
  /**
49
44
  * A variable assignment, in every form make accepts (`=`, `:=`, `::=`, `?=`, `+=`,
50
45
  * `!=`, with an optional `override`/`export`). Matched BEFORE the target rule
51
- * because `FLAGS ::= -O2` otherwise reads as a target named `FLAGS`.
46
+ * because `FLAGS ::= -O2` otherwise reads as a target named `FLAGS` — make itself
47
+ * reports that line as a variable, not a rule.
52
48
  */
53
49
  const ASSIGN_RE = /^(?:override\s+|export\s+)?[A-Za-z_][A-Za-z0-9_.]*\s*(?:::|:|\?|\+|!)?=/;
54
50
  /**
@@ -87,9 +83,10 @@ export function makeTargets(src) {
87
83
  }
88
84
  /**
89
85
  * The makefiles that really exist in `cwd`, in GNU make's lookup order, named as
90
- * the DIRECTORY spells them. `existsSync` cannot do this job: on a case-insensitive
91
- * filesystem (Windows, macOS) `existsSync('makefile')` is true for a `Makefile`, and
92
- * the failure text would then name a file the project does not have.
86
+ * the DIRECTORY spells them. The invariant is that `manifest.file` is always a name
87
+ * `readdirSync(cwd)` contains, so a failure can never name a file the project does
88
+ * not have. An `existsSync` probe per candidate name cannot promise that, because it
89
+ * answers about a name we chose rather than a name on disk.
93
90
  */
94
91
  function makefilesOnDisk(cwd) {
95
92
  let entries;
@@ -141,7 +138,7 @@ export function readLaunchManifest(cwd) {
141
138
  }
142
139
  /**
143
140
  * The one line an INERT contract leaves behind. It rides in the gate's UNOBSERVED
144
- * channel rather than in `warnings` for the reason nexttask 16A gives: a check that
141
+ * channel rather than in `warnings` for one reason: a check that
145
142
  * silently did nothing must not read later as a check that passed.
146
143
  */
147
144
  export function inertLaunchContractNote(declared, manifest) {
@@ -160,7 +157,11 @@ export function packageScripts(cwd) {
160
157
  return {};
161
158
  }
162
159
  }
163
- /** Does this tree's Makefile declare `target`? */
160
+ /**
161
+ * Does this tree's Makefile declare `target`? Reads the literal name `Makefile`
162
+ * only — unlike {@link readLaunchManifest}, this one does not walk make's lookup
163
+ * order, so a `GNUmakefile`-only tree answers false.
164
+ */
164
165
  export function makeHasTarget(cwd, target) {
165
166
  try {
166
167
  const mk = readFileSync(path.join(cwd, 'Makefile'), 'utf8');
@@ -3,24 +3,26 @@
3
3
  * `.pi-tasks/` (contracts, launch-contract, env-notes, accept-debt, repair-queue,
4
4
  * requirements, requirements-owned).
5
5
  *
6
- * Six modules each kept their own copy of the same seven steps: read the file
7
- * (ANY error → ''), parse it into records, key the records, drop an incoming item
8
- * whose key is already present, cap to the newest MAX (oldest dropped), mkdir the
9
- * tasks dir, write the whole file back (`lines.join('\n') + '\n'`, plain
10
- * `writeFile`, NOT atomic), and swallow every fault a ledger is a sharpener or
11
- * an auditing aid, never a blocker of the phase or gate that calls it. What
12
- * genuinely varied per site is the DATA SHAPE (file name, cap, key, line format,
13
- * parser) and exactly one RULE what an append does when it adds nothing new
14
- * (see `onNoop`). Everything else here is the ritual, so a module that keeps a
15
- * ledger is an ADAPTER: it declares its ledger and calls read/append/write.
6
+ * The ritual is seven steps: read the file (ANY error ''), parse it into records,
7
+ * key the records, drop an incoming item whose key is already present, cap to the
8
+ * newest MAX (oldest dropped), mkdir the tasks dir, write the whole file back
9
+ * (`lines.join('\n') + '\n'`, plain `writeFile`, NOT atomic). Every fault is
10
+ * swallowed a ledger is a sharpener or an auditing aid, never a blocker of the
11
+ * phase or gate that calls it.
12
+ *
13
+ * What varies per site is the DATA SHAPE (file name, cap, key, line format, parser)
14
+ * and exactly one RULE what an append does when it adds nothing new (see
15
+ * `onNoop`). Everything else here is the ritual, so a module that keeps a ledger is
16
+ * an ADAPTER: it declares its ledger and calls read/append/write.
16
17
  *
17
18
  * Contract details a caller can rely on:
18
19
  * • `readRaw` is the trimmed file text ('' when absent or unreadable). Prompt-block
19
20
  * builders take this string.
20
- * • `read` is `parse(readRaw)`; every parser skips blank lines and lines it cannot
21
- * read, so a corrupt line is dropped, never thrown on.
22
- * • `append` with an empty batch is a no-op (no read, no write). Within a batch
23
- * the first item with a key wins; a key already stored wins over the batch.
21
+ * • `read` is `parse(readRaw)`; every adapter's parser skips blank lines and lines
22
+ * it cannot read, so a corrupt line is dropped, never thrown on.
23
+ * • `append` with an empty batch is a no-op it does not even create `.pi-tasks/`.
24
+ * Within a batch the first item with a key wins; a key already stored wins over
25
+ * the batch.
24
26
  * • `write` overwrites with exactly these records; an empty list writes an empty
25
27
  * file (this is how a drained queue and a fully-resolved debt ledger look).
26
28
  * • Neither `append` nor `write` ever throws.