@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
@@ -6,9 +6,6 @@ import { isYoloMode, yoloFinalGateChoice, YOLO_STAMP } from './yolo.js';
6
6
  import { ignoredWriteTrailLine, ignoredWriteDebtReason } from './write-guard.js';
7
7
  import { readOwnedRequirements } from './requirements.js';
8
8
  import { unclaimedPendingRequirements } from './owned-freeze-reassign.js';
9
- // `final-gate-progress.ts`'s five pure functions are `AutofixLedger`'s internals
10
- // now — each was called from exactly ONE site inside this loop, extracted for
11
- // testability while the ordering and carry-forward decisions stayed out here.
12
9
  import { classifyFinalGateAnswer, MAX_FINAL_GATE_AUTOFIX, FINAL_LEAVE_LABEL, FINAL_LEAVE_VALUE, FINAL_ACCEPT_LABEL, FINAL_ACCEPT_VALUE, FINAL_AUTOFIX_LABEL, FINAL_AUTOFIX_VALUE, STRANDED_FIX_COMMIT, strandedFixNote } from './final-gate-fix.js';
13
10
  /**
14
11
  * Show the run-end picker and return the raw answer. Card ORDER is fixed —
@@ -49,10 +46,9 @@ function completedResult(id, taskCount, unobservedNote) {
49
46
  /**
50
47
  * Run the whole-repo gate and resolve its verdict with the user.
51
48
  *
52
- * Lifted verbatim out of /task-auto's run loop, which it never shared state with:
53
- * the stage reads no per-task variable and writes none. Never throws for a gate
54
- * outcome — only a user cancel inside a gate child propagates (the caller's
55
- * USER_CANCELLED path handles it).
49
+ * Shares no state with /task-auto's run loop: the stage reads no per-task variable
50
+ * and writes none. Never throws for a gate outcome — only a user cancel inside a
51
+ * gate child propagates (the caller's USER_CANCELLED path handles it).
56
52
  */
57
53
  export async function runFinalGateStage(active, deps, p) {
58
54
  const { cwd, runId: id, planText, taskCount } = p;
@@ -91,9 +87,8 @@ export async function runFinalGateStage(active, deps, p) {
91
87
  // recording must never break the gate
92
88
  }
93
89
  };
94
- // Trail EVERY aggregated failure entry (mx5 run 13): the gate runs all sections
95
- // and ranks the list; a single sliced reason line would re-hide everything past
96
- // the first entry.
90
+ // Trail EVERY aggregated failure entry: the gate runs all sections and ranks the
91
+ // list, so a single sliced reason line would hide everything past the first.
97
92
  const trailGateFail = async (f) => {
98
93
  const list = f.failures ?? [f.reason];
99
94
  if (list.length <= 1) {
@@ -106,14 +101,12 @@ export async function runFinalGateStage(active, deps, p) {
106
101
  }
107
102
  };
108
103
  let fin = await deps.finalGate(cwd, planText);
109
- // Record the outcome symmetrically (mx5 run 10 item 7): only FAIL was ever
110
- // trailed, so a PASSing gate was indistinguishable from a gate that never ran. The
111
- // PASS reason names the commands that were run. THREE verdicts, not two
112
- // (final-gate.ts unobservedVerdict): a gate that observed nothing dynamic is
113
- // UNOBSERVED, never PASS IAR1 shipped `PASS no integration command found`
114
- // twice while carrying open verify-FAIL debt. It does not block (justified there),
115
- // but it is labelled here, warned about, and recorded as durable debt so the next
116
- // run's gate re-surfaces it.
104
+ // Record the outcome SYMMETRICALLY trailing only FAIL would leave a passing
105
+ // gate indistinguishable from a gate that never ran, and the PASS reason names
106
+ // the commands that were run. THREE verdicts, not two (gate-tally.ts
107
+ // `unobservedVerdict`): a gate that observed nothing dynamic is UNOBSERVED, never
108
+ // PASS. It does not block, but it is labelled here, warned about, and recorded as
109
+ // durable debt so the next run's gate re-surfaces it.
117
110
  if (fin.ok && fin.unobserved) {
118
111
  unobservedNote = fin.unobserved;
119
112
  await recGate(`final-gate: UNOBSERVED — ${fin.reason.slice(0, 300)}`);
@@ -128,9 +121,8 @@ export async function runFinalGateStage(active, deps, p) {
128
121
  else {
129
122
  await trailGateFail(fin);
130
123
  }
131
- // ACCEPT-debt re-check surfacing (mx5 run 4 B3 / run 8 TASK_0012): tasks the user
132
- // accepted despite a verify-FAIL that the gate could not prove resolved against the
133
- // current tree. Surface them at the gate moment — on PASS or FAIL — so a run never
124
+ // ACCEPT-debt surfacing: tasks the user accepted despite a verify-FAIL that the
125
+ // gate could not prove resolved against the current tree. Surface them at the gate moment on PASS or FAIL — so a run never
134
126
  // completes silently carrying an accepted defect. Informational: the per-task
135
127
  // ACCEPT was already a human decision, so this reports, it does not re-fail.
136
128
  const debtKey = (d) => `${d.taskId}\t${d.reason}`;
@@ -146,10 +138,10 @@ export async function runFinalGateStage(active, deps, p) {
146
138
  // rather than blindly re-printed.
147
139
  let reportedDebts = fin.openDebts ?? [];
148
140
  await surfaceOpenDebts(reportedDebts);
149
- // An owned obligation a task DETACHED (its own spec froze the only file that could
150
- // satisfy it, nexttask 2) and no later task claimed. Detach never deletes the
151
- // quote, so the run ends holding it say so, or the resolution would be a quieter
152
- // version of the deletion it exists to prevent.
141
+ // An owned obligation a task DETACHED its own spec froze the only file that
142
+ // could satisfy it and no later task claimed. Detach never deletes the quote, so
143
+ // the run ends holding it. Say so, or the detach becomes a quieter version of the
144
+ // deletion it exists to prevent.
153
145
  const unclaimed = unclaimedPendingRequirements(await (deps.ownedRequirements ?? readOwnedRequirements)(cwd).catch(() => []));
154
146
  for (const o of unclaimed) {
155
147
  await recGate(`owned requirement UNCLAIMED — "${o.quote.slice(0, 200)}"`
@@ -162,12 +154,11 @@ export async function runFinalGateStage(active, deps, p) {
162
154
  + ' file, and nothing else claimed it. See the gate trail.', 'warning');
163
155
  }
164
156
  /**
165
- * nexttask 6 (mx5 run 18). The lines above are emitted from the FIRST gate result;
166
- * the converged-autofix paths below used to rebuild `fin` as `{ok, reason}`, so
167
- * `openDebts` did not survive the fix pass — the run's last word on its own defects
168
- * was a snapshot of a tree that no longer existed, and no code path could clear,
169
- * re-check or act on it. Re-derive here, against the tree the run actually ends
170
- * with, and correct the record.
157
+ * Re-derive the debt report against the tree the run actually ends with.
158
+ *
159
+ * The lines above are emitted from the FIRST gate result, so without this the
160
+ * run's last word on its own defects describes a tree the autofix has since
161
+ * changed.
171
162
  *
172
163
  * FP-safe by inheritance: `deriveOpenDebts` auto-closes only what a deterministic
173
164
  * check can stand behind (a static-class debt when the statics provably pass, a
@@ -192,7 +183,7 @@ export async function runFinalGateStage(active, deps, p) {
192
183
  openDebts: fresh.openDebts,
193
184
  ...(fresh.debtNote ? { debtNote: fresh.debtNote } : {})
194
185
  };
195
- // Per-debt evidence from the VERIFY-COMMAND re-check (nexttask 5): which
186
+ // Per-debt evidence from the VERIFY-COMMAND re-check: which
196
187
  // command was re-run and what it did. A close that cannot be read back from
197
188
  // the trail is a close nobody can audit, and an INCONCLUSIVE re-run is worth
198
189
  // saying out loud — it is the difference between "still broken" and "nothing
@@ -226,16 +217,15 @@ export async function runFinalGateStage(active, deps, p) {
226
217
  await recGate(`defect re-check after autofix: ${closed.length} resolved, `
227
218
  + `${fresh.openDebts.length} still open (re-derived against the FINAL tree)`);
228
219
  };
229
- // Resolution loop: Leave-failed (recommended) / Autofix (bounded, model-driven fix
230
- // pass + gate re-run run 7's gap: the picker had NO automated fix path) /
231
- // Accept. The user always decides; after MAX_FINAL_GATE_AUTOFIX attempts that
232
- // still FAIL the autofix card is withdrawn so the loop cannot run unbounded.
233
- // What this loop RECORDS, and the decisions that record makes: the attempt
234
- // count and its bound, the accumulated gitignored writes, the stranded
220
+ // Resolution loop: Leave-failed (recommended) / Autofix (bounded, model-driven
221
+ // fix pass + gate re-run) / Accept. The user always decides; after
222
+ // MAX_FINAL_GATE_AUTOFIX attempts that still FAIL the autofix card is withdrawn,
223
+ // so the loop cannot run unbounded.
224
+ //
225
+ // Everything the loop remembers lives in the ledger, not in closure locals: the
226
+ // attempt count and its bound, the accumulated gitignored writes, the stranded
235
227
  // sub-fixes, the previous failure signature, the demoted set and the
236
- // rejected-edits flag. Six closure-threaded locals before, and the
237
- // non-progress rule applied downstream from the evidence it judges — the shape
238
- // `final-gate-progress.ts`'s own comment names as the run-21 defect.
228
+ // rejected-edits flag. That keeps the non-progress rule where its evidence is.
239
229
  // `GateTally`'s twin, one altitude up (autofix-ledger.ts).
240
230
  const ledger = new AutofixLedger(MAX_FINAL_GATE_AUTOFIX);
241
231
  const refreshStranded = async () => {
@@ -250,9 +240,8 @@ export async function runFinalGateStage(active, deps, p) {
250
240
  }
251
241
  };
252
242
  // Commit whatever guard-clean repairs the fix passes left, on ANY terminal
253
- // non-converged outcome. Run 14 ended on LEAVE with 13 real repairs dirty in the
254
- // tree after an unattended run — the next checkout would have destroyed them
255
- // silently.
243
+ // non-converged outcome. A run that ends on LEAVE can hold real repairs dirty in
244
+ // the tree, and the next checkout would destroy them silently.
256
245
  const commitStranded = async (outcome) => {
257
246
  const stranded = ledger.stranded();
258
247
  if (stranded.length === 0)
@@ -263,13 +252,11 @@ export async function runFinalGateStage(active, deps, p) {
263
252
  + `so the tree holds REJECTED edits: ${stranded.slice(0, 8).join(', ')}`);
264
253
  return;
265
254
  }
266
- // REPORT WHAT ACTUALLY HAPPENED (mx5 run 20). This bound the CommitResult to
267
- // `sha` and interpolated it, so the trail read "committed 5 stranded fix-pass
268
- // change(s) as [object Object]". Worse than cosmetic: `commit` returns
269
- // {committed, reason?, note?} and NEVER a sha, the `committed` field was never
270
- // read, and gitCommitAll returns {committed:false} WITHOUT throwing on an
271
- // unmerged index — so on that path the catch below never fires and the trail
272
- // claimed a commit over changes that were still sitting in the working tree.
255
+ // REPORT WHAT ACTUALLY HAPPENED, which means reading `committed`. A
256
+ // CommitResult is {committed, reason?, note?} and carries NO sha, and
257
+ // gitCommitAll answers {committed:false} WITHOUT throwing so a trail line
258
+ // written from the catch alone would claim a commit over changes still sitting
259
+ // in the working tree.
273
260
  const notCommitted = async (why) => {
274
261
  await recGate(`final-gate: could NOT commit ${stranded.length} stranded fix-pass `
275
262
  + `change(s) (${why}) — they remain UNCOMMITTED in the working `
@@ -293,25 +280,23 @@ export async function runFinalGateStage(active, deps, p) {
293
280
  };
294
281
  while (!fin.ok) {
295
282
  const canAutofix = deps.finalGateFix !== undefined && ledger.canAutofix();
296
- // The picker question shows the debts (the HUMAN weighs them); the autofix seed
297
- // below deliberately does not mx5 run 11's fix child executed a debt claim as
298
- // an `rm` instruction.
283
+ // The picker question shows the debts, because the HUMAN weighs them. The
284
+ // autofix seed below deliberately does not: a debt claim is prose about a
285
+ // defect, and a write-enabled child reads prose as an instruction.
299
286
  const question = `Final integration gate FAILED for ${id}.\n\n${fin.reason}${fin.debtNote ?? ''}\n\n`
300
287
  + 'All tasks are checked off — this is the whole-repo check '
301
288
  + '(the project’s own test/build/static commands, run unaided).'
302
289
  + (ledger.attempts() > 0 ?
303
290
  `\n\nAutofix attempts so far: ${ledger.attempts()}/${MAX_FINAL_GATE_AUTOFIX}.`
304
291
  : '')
305
- // Never let a partial repair be invisible at the moment the human decides
306
- // (run 13: a bunfig fix that made `bun run test` pass 116/116 was stranded
307
- // by an ACCEPT).
292
+ // Never let a partial repair be invisible at the moment the human
293
+ // decides an ACCEPT here would otherwise strand a real fix.
308
294
  + strandedFixNote([...ledger.stranded()]);
309
295
  // YOLO: keep autofixing WHILE the card is still offered — the loop withdraws it
310
296
  // after MAX_FINAL_GATE_AUTOFIX, so the cap that bounds a non-converging fix pass
311
297
  // still bounds this — then LEAVE the run failed. Never 'accept': an unattended
312
298
  // run that could not green the whole-repo gate has not produced a working
313
- // project, and mx5 run 13 shows what an accepted FAIL looks like afterwards (a
314
- // shipped app that 404s at `/`).
299
+ // project, and nobody is there to judge the FAIL it would be accepting.
315
300
  const yoloFinal = yoloFinalGateChoice(isYoloMode(), canAutofix);
316
301
  if (yoloFinal !== null) {
317
302
  await recGate(`final-gate: auto-chose ${yoloFinal.action.toUpperCase()} ${YOLO_STAMP}`);
@@ -328,7 +313,7 @@ export async function runFinalGateStage(active, deps, p) {
328
313
  // repaired but never committed would be lost to the next `git checkout`
329
314
  // while HEAD keeps the defect it fixed. Commit it as its own, named
330
315
  // commit — the ACCEPT is a decision about the FAILING gate, never an
331
- // instruction to throw away work (mx5 run 13 item 3).
316
+ // instruction to throw away work.
332
317
  await commitStranded('accepted');
333
318
  active.ui.notify(`${id}: final integration gate FAIL accepted by user — completing.`
334
319
  + (ledger.stranded().length > 0 ?
@@ -342,7 +327,7 @@ export async function runFinalGateStage(active, deps, p) {
342
327
  active.ui.notify(`${id}: final-gate autofix (${attempt}/${MAX_FINAL_GATE_AUTOFIX}) — bounded fix pass, then the gate re-runs…`, 'info');
343
328
  const seed = choice.guidance ? `${fin.reason}\n\nUser guidance: ${choice.guidance}` : fin.reason;
344
329
  const fix = await deps.finalGateFix(active, cwd, seed, [...ledger.ignoredWrites()]);
345
- // IGNORED-PATH WRITES (mx5 run 19). The pass wrote file(s) git ignores, so
330
+ // IGNORED-PATH WRITES. The pass wrote file(s) git ignores, so
346
331
  // they are not in the commit and a fresh clone does not have them. Trailed
347
332
  // on EVERY outcome — a rejected attempt's tracked edits are discarded while
348
333
  // its ignored writes survive on disk — and carried forward, so a later
@@ -371,10 +356,9 @@ export async function runFinalGateStage(active, deps, p) {
371
356
  }
372
357
  await recGate(`final-gate: autofix ${fix.unobserved ? 'ended UNOBSERVED' : 'converged'} — ${fix.reason.slice(0, 200)}`);
373
358
  active.ui.notify(`${id}: final integration gate ${fix.unobserved ? 'is UNOBSERVED' : 'PASSES'} after autofix — ${fix.reason.slice(0, 140)}`, fix.unobserved ? 'warning' : 'info');
374
- // The gate's own outcome, whole, with this door's reason on it. It
375
- // used to be a two-key literal, so `openDebts` and `observedFailures`
376
- // were dropped and `reconcileDebts` was the only thing putting one of
377
- // them back — the recorded mx5 run-18 defect.
359
+ // The gate's own outcome, WHOLE, with this door's reason on it.
360
+ // Rebuilding it as a two-key literal would drop `openDebts` and
361
+ // `observedFailures` from the value entirely.
378
362
  fin = { ...(fix.gate ?? fin), ok: true, reason: fix.reason };
379
363
  // The gate itself just passed, statics included, so `staticOk` here is
380
364
  // proof rather than assumption.
@@ -397,12 +381,12 @@ export async function runFinalGateStage(active, deps, p) {
397
381
  + `uncommitted change(s) — ${ledger.stranded().slice(0, 8).join(', ')}`);
398
382
  }
399
383
  active.ui.notify(`${id}: final-gate autofix did not converge — ${fix.reason.slice(0, 140)}`, 'warning');
400
- // NON-PROGRESS CLASSIFIER (mx5 run 14 item 2a). An attempt that changed the
401
- // tree, re-ran the gate, and got back the SAME ranked-first failure as the
402
- // previous such attempt is evidence about the CHECK, not the fix: run 14
403
- // burned all three attempts on a boot probe that could not observe a
404
- // listener in that sandbox at all. Demote that one check to
405
- // UNOBSERVED-with-debt and let the REMAINING checks decide.
384
+ // NON-PROGRESS CLASSIFIER. An attempt that changed the tree, re-ran the
385
+ // gate, and got back the SAME ranked-first failure as the previous such
386
+ // attempt is evidence about the CHECK, not about the fix a check that
387
+ // cannot observe anything in this environment answers identically however
388
+ // the tree moves. Demote that one check to UNOBSERVED-with-debt and let
389
+ // the REMAINING checks decide.
406
390
  //
407
391
  // The judgement, the observed check and the signature carry-forward are
408
392
  // the ledger's — made where the evidence is, not downstream from it.
@@ -446,12 +430,10 @@ export async function runFinalGateStage(active, deps, p) {
446
430
  // next picker and the next fix seed target only what is still falsifiable —
447
431
  // never re-aiming the child at the check the classifier just proved it
448
432
  // cannot move.
449
- // Outcome to outcome. The base is the FRESH gate outcome when the fix
450
- // pass got as far as re-running it, otherwise the one we already hold
451
- // and either way it arrives whole, so nothing (`openDebts`,
452
- // `observedFailures`) is dropped by the assignment. This used to be a
453
- // literal with four keys, and the field it omitted is the recorded mx5
454
- // run-18 defect.
433
+ // Outcome to outcome, WHOLE. The base is the FRESH gate outcome when the
434
+ // fix pass got as far as re-running it, otherwise the one we already hold;
435
+ // either way it is spread rather than rebuilt, so no field (`openDebts`,
436
+ // `observedFailures`) is dropped by the assignment.
455
437
  const base = fix.gate ?? fin;
456
438
  const carried = base.failures === undefined ? undefined : ledger.remaining(base);
457
439
  fin = {
@@ -477,9 +459,9 @@ export async function runFinalGateStage(active, deps, p) {
477
459
  `final-gate: left failed — autofix budget spent, nobody to ask ${YOLO_STAMP}`
478
460
  : 'final-gate: left failed (user)');
479
461
  // Leaving the run failed is TERMINAL for an unattended run, so the fix passes'
480
- // guard-clean repairs are committed here too run 14 left 13 of them dirty for
481
- // a `git checkout` to destroy (mx5 run 13 item 3, run 14 item 2b). The user
482
- // still owns the outcome; they own it with the work in HEAD, named in the trail.
462
+ // guard-clean repairs are committed here too, rather than left dirty for a
463
+ // `git checkout` to destroy. The user still owns the outcome; they own it with
464
+ // the work in HEAD, named in the trail.
483
465
  await commitStranded('left-failed');
484
466
  return {
485
467
  kind: 'failed',
@@ -2,18 +2,16 @@
2
2
  * runner-globs — deterministic detection of TWO TEST RUNNERS FIGHTING OVER THE SAME
3
3
  * FILES, checked as soon as a project declares both rather than discovered at run end.
4
4
  *
5
- * The failure this closes — SECOND occurrence, runs 7 AND 13: a project declares both
6
- * `bun test` and `playwright test`. Bun's runner scans the whole project for
7
- * `*.test.*` / `*.spec.*`; Playwright's component/e2e specs ARE `*.spec.tsx`. So
8
- * `bun test` imports Playwright spec files, which import `@playwright/test` outside a
9
- * Playwright runner, and the whole suite dies on a module it was never meant to load.
5
+ * The failure this closes: a project declares both `bun test` and
6
+ * `playwright test`. `bun test` with no arguments scans the whole project tree for
7
+ * `*.test.*` / `*.spec.*`, and Playwright's component/e2e specs ARE `*.spec.tsx`.
8
+ * So `bun test` imports Playwright spec files, which import `@playwright/test`
9
+ * outside a Playwright runner, and the whole suite dies on a module it was never
10
+ * meant to load.
10
11
  *
11
- * Run 7 found it in the final gate. Run 13 found it in the final gate AGAIN and the
12
- * fix (a `pathIgnorePatterns` line in bunfig.toml) was still sitting UNCOMMITTED in
13
- * the working tree when the run ended, so HEAD shipped with `bun run test` broken. A
14
- * defect that recurs across runs and survives its own fix is not a discovery problem;
15
- * it is a missing invariant. This module states the invariant so it can be checked the
16
- * moment both runners are declared:
12
+ * Finding that at the final gate is too late: the fix is a one-line config change
13
+ * that can still be sitting uncommitted when the run ends. So the invariant is
14
+ * checked the moment both runners are declared:
17
15
  *
18
16
  * if two runners are declared, their file sets must be provably DISJOINT
19
17
  *
@@ -51,8 +49,9 @@ export interface RunnerGlobInputs {
51
49
  */
52
50
  export declare function parsePathIgnorePatterns(bunfig: string | null): string[] | null;
53
51
  /**
54
- * Playwright's `testMatch`, when the config states one. Null the default, which
55
- * matches `*.spec.*` and `*.test.*` precisely Bun's claimed set.
52
+ * Playwright's `testMatch`, when the config states one. Null when the config
53
+ * states none (or there is no config), in which case the NAMING form cannot be
54
+ * proven and `assessRunnerGlobs` falls through to the exclusion check.
56
55
  */
57
56
  export declare function parseTestMatch(playwrightConfig: string | null): string[] | null;
58
57
  /** Playwright's `testDir`, when stated (used to explain the collision concretely). */
@@ -2,18 +2,16 @@
2
2
  * runner-globs — deterministic detection of TWO TEST RUNNERS FIGHTING OVER THE SAME
3
3
  * FILES, checked as soon as a project declares both rather than discovered at run end.
4
4
  *
5
- * The failure this closes — SECOND occurrence, runs 7 AND 13: a project declares both
6
- * `bun test` and `playwright test`. Bun's runner scans the whole project for
7
- * `*.test.*` / `*.spec.*`; Playwright's component/e2e specs ARE `*.spec.tsx`. So
8
- * `bun test` imports Playwright spec files, which import `@playwright/test` outside a
9
- * Playwright runner, and the whole suite dies on a module it was never meant to load.
5
+ * The failure this closes: a project declares both `bun test` and
6
+ * `playwright test`. `bun test` with no arguments scans the whole project tree for
7
+ * `*.test.*` / `*.spec.*`, and Playwright's component/e2e specs ARE `*.spec.tsx`.
8
+ * So `bun test` imports Playwright spec files, which import `@playwright/test`
9
+ * outside a Playwright runner, and the whole suite dies on a module it was never
10
+ * meant to load.
10
11
  *
11
- * Run 7 found it in the final gate. Run 13 found it in the final gate AGAIN and the
12
- * fix (a `pathIgnorePatterns` line in bunfig.toml) was still sitting UNCOMMITTED in
13
- * the working tree when the run ended, so HEAD shipped with `bun run test` broken. A
14
- * defect that recurs across runs and survives its own fix is not a discovery problem;
15
- * it is a missing invariant. This module states the invariant so it can be checked the
16
- * moment both runners are declared:
12
+ * Finding that at the final gate is too late: the fix is a one-line config change
13
+ * that can still be sitting uncommitted when the run ends. So the invariant is
14
+ * checked the moment both runners are declared:
17
15
  *
18
16
  * if two runners are declared, their file sets must be provably DISJOINT
19
17
  *
@@ -53,8 +51,9 @@ export function parsePathIgnorePatterns(bunfig) {
53
51
  return [...m[1].matchAll(/["']([^"']+)["']/g)].map(x => x[1]);
54
52
  }
55
53
  /**
56
- * Playwright's `testMatch`, when the config states one. Null the default, which
57
- * matches `*.spec.*` and `*.test.*` precisely Bun's claimed set.
54
+ * Playwright's `testMatch`, when the config states one. Null when the config
55
+ * states none (or there is no config), in which case the NAMING form cannot be
56
+ * proven and `assessRunnerGlobs` falls through to the exclusion check.
58
57
  */
59
58
  export function parseTestMatch(playwrightConfig) {
60
59
  if (playwrightConfig === null)
@@ -24,13 +24,12 @@ export declare function resolveRunner(bin: string, opts?: {
24
24
  * Output shapes a RUNNER emits when the command inside a script chain does not
25
25
  * exist, on platforms where that is not reported as exit 127.
26
26
  *
27
- * 127 is a POSIX-SHELL convention: on Linux/macOS bun hands the script to
28
- * /bin/sh, the shell prints `…: command not found` and exits 127, and the whole
29
- * env-gap contract keys off that number. On Windows there is no such shell
30
- * bun runs the script in its own built-in shell, which reports the miss itself
31
- * (`bun: command not found: X`) and exits **1**, indistinguishable by status
32
- * alone from a real code fault. cmd.exe (9009) and PowerShell have their own
33
- * wording. Recognising the shape restores one env-gap contract on all three.
27
+ * 127 is a POSIX-SHELL convention: bun hands the script to a shell, the shell
28
+ * prints `…: command not found` and exits 127, and the whole env-gap contract
29
+ * keys off that number. Where no such shell runs the script, the runner reports
30
+ * the miss itself (`bun: command not found: X`) and the status alone cannot be
31
+ * told from a real code fault. cmd.exe (9009) and PowerShell have their own
32
+ * wording. Recognising the shape restores the env-gap contract there.
34
33
  *
35
34
  * Deliberately narrow: only wordings a RUNNER/SHELL produces, never the bare
36
35
  * phrase. A suite that prints "command not found" inside a failing assertion is
@@ -41,8 +40,9 @@ export declare const COMMAND_NOT_FOUND_OUTPUT_RE: RegExp;
41
40
  * Did this command fail because the thing it tried to run does not exist here,
42
41
  * rather than because the code is wrong? Exit 127 (POSIX shell) or 9009
43
42
  * (cmd.exe) say so outright; anything else needs the runner's own wording (see
44
- * COMMAND_NOT_FOUND_OUTPUT_RE) a Windows `bun run dev` on a missing binary
45
- * exits 1. Callers treat a true here as an environment gap → skip, never FAIL.
43
+ * COMMAND_NOT_FOUND_OUTPUT_RE), because a runner that resolves the command
44
+ * itself can report the miss and still exit 1. Callers treat a true here as an
45
+ * environment gap → skip, never FAIL.
46
46
  */
47
47
  export declare function isCommandNotFound(status: number | null, output?: string): boolean;
48
48
  /**
@@ -2,26 +2,25 @@
2
2
  * runner-resolve — make the deterministic gates able to SPAWN the project's own
3
3
  * runner when the host PATH lost it.
4
4
  *
5
- * The failure this closes (mx5 run 16, validated): pi was launched inside the
6
- * sandbox through a LOGIN shell, whose /etc/profile reset PATH and dropped
7
- * ~/.bun/bin — so `bun` was unspawnable in every gate spawn. Under the env-gap
8
- * contract (ENOENT / exit 127 → skip, deliberately, so a missing tool is never a
9
- * code fault) EVERY dynamic check silently skipped: bun test, the boot of
10
- * `bun run dev`, and therefore the render check built for exactly the blank-page
11
- * class the run shipped. The gate converged on static checks alone and stamped
12
- * the run green while the binary sat at ~/.bun/bin/bun the whole time.
5
+ * The failure this closes: a host whose PATH lost the runner pi launched
6
+ * through a login shell whose profile rebuilt PATH without `~/.bun/bin`, say.
7
+ * `bun` is then unspawnable in every gate spawn, and under the env-gap contract
8
+ * (ENOENT / exit 127 → skip, deliberately, so a missing tool is never a code
9
+ * fault) EVERY dynamic check silently skips: the test command, the boot of
10
+ * `bun run dev`, and therefore the render check too. The gate converges on static
11
+ * checks alone and stamps the run green while the binary sits in a directory
12
+ * nothing looked in.
13
13
  *
14
14
  * Resolution is discovery, never installation: try the bare name first (PATH
15
15
  * serves it → nothing changes), then probe well-known install locations. Each
16
16
  * probe is a real `<candidate> --version` spawn — an existing but broken binary
17
17
  * must not count as resolved.
18
18
  *
19
- * The PATH PREFIX matters as much as the binary: a resolved `bun run test` still
20
- * re-invokes `bun` (and the repo's own bins) INSIDE the script chain, and those
21
- * inner calls exit 127 without the runner's directory on PATH the same silent
22
- * blindness one level down (run 16's final-fix child hit exactly this and had to
23
- * hand-export PATH). Spawn sites must therefore use runnerEnv(), not just the
24
- * resolved binary.
19
+ * {@link runnerEnv} prepends the resolved directory to PATH for the spawned
20
+ * script chain. Spawn sites should use it rather than the resolved binary alone:
21
+ * a `bun run test` re-invokes commands INSIDE the script, and a runner that does
22
+ * not put itself within reach of those inner calls would fail them for the same
23
+ * reason the outer spawn failed.
25
24
  */
26
25
  import { spawnSync } from 'node:child_process';
27
26
  import { existsSync } from 'node:fs';
@@ -87,13 +86,12 @@ export function resolveRunner(bin, opts = {}) {
87
86
  * Output shapes a RUNNER emits when the command inside a script chain does not
88
87
  * exist, on platforms where that is not reported as exit 127.
89
88
  *
90
- * 127 is a POSIX-SHELL convention: on Linux/macOS bun hands the script to
91
- * /bin/sh, the shell prints `…: command not found` and exits 127, and the whole
92
- * env-gap contract keys off that number. On Windows there is no such shell
93
- * bun runs the script in its own built-in shell, which reports the miss itself
94
- * (`bun: command not found: X`) and exits **1**, indistinguishable by status
95
- * alone from a real code fault. cmd.exe (9009) and PowerShell have their own
96
- * wording. Recognising the shape restores one env-gap contract on all three.
89
+ * 127 is a POSIX-SHELL convention: bun hands the script to a shell, the shell
90
+ * prints `…: command not found` and exits 127, and the whole env-gap contract
91
+ * keys off that number. Where no such shell runs the script, the runner reports
92
+ * the miss itself (`bun: command not found: X`) and the status alone cannot be
93
+ * told from a real code fault. cmd.exe (9009) and PowerShell have their own
94
+ * wording. Recognising the shape restores the env-gap contract there.
97
95
  *
98
96
  * Deliberately narrow: only wordings a RUNNER/SHELL produces, never the bare
99
97
  * phrase. A suite that prints "command not found" inside a failing assertion is
@@ -104,8 +102,9 @@ export const COMMAND_NOT_FOUND_OUTPUT_RE = /\b(?:bun|npm|pnpm|yarn|node|deno): c
104
102
  * Did this command fail because the thing it tried to run does not exist here,
105
103
  * rather than because the code is wrong? Exit 127 (POSIX shell) or 9009
106
104
  * (cmd.exe) say so outright; anything else needs the runner's own wording (see
107
- * COMMAND_NOT_FOUND_OUTPUT_RE) a Windows `bun run dev` on a missing binary
108
- * exits 1. Callers treat a true here as an environment gap → skip, never FAIL.
105
+ * COMMAND_NOT_FOUND_OUTPUT_RE), because a runner that resolves the command
106
+ * itself can report the miss and still exit 1. Callers treat a true here as an
107
+ * environment gap → skip, never FAIL.
109
108
  */
110
109
  export function isCommandNotFound(status, output = '') {
111
110
  if (status === 127 || status === 9009)
@@ -1,25 +1,24 @@
1
1
  /**
2
2
  * script-escape — deterministic detection of CHECK SCRIPTS THAT CANNOT FAIL.
3
3
  *
4
- * The failure this closes (mx5 run 13, PROMPT 4 item 4): the shipped package.json
5
- * declared, verbatim,
4
+ * The failure this closes: a package.json declaring
6
5
  * "lint": "prettier … && eslint --fix … && (tsc --noEmit 2>&1 | grep -qv 'TS18003' || true)"
7
6
  * The typecheck is neutered twice over — its output is piped into an INVERTED grep
8
7
  * (so the status becomes "some line did not match", never tsc's verdict), and the
9
8
  * whole group is closed with `|| true` (so the script exits 0 unconditionally). Every
10
9
  * consumer of that script — the repo-health verify gate, the final integration gate,
11
- * a human reading a green CI line — is reading a constant, not a measurement.
10
+ * a human reading a green CI line — is reading a constant, not a measurement. It
11
+ * looks fine for exactly as long as the checker happens to be clean.
12
12
  *
13
- * It was harmless in run 13 only by luck: tsc happened to be clean (validated). The
14
- * class is not harmless this is the same defect as run-8's F2 skip-escape, moved
15
- * one level out. findSkipEscapes (skip-escape.ts) scans a spec's own VERIFY block;
16
- * nothing scanned the SCRIPT DEFINITIONS those VERIFY blocks then invoke by name, so
17
- * `bun run lint` could be authored into a no-op and every gate would salute it.
13
+ * This is a skip-escape moved one level out. `findSkipEscapes` (skip-escape.ts)
14
+ * scans a spec's own VERIFY block; nothing else scans the SCRIPT DEFINITIONS those
15
+ * VERIFY blocks then invoke by name, so `bun run lint` can be authored into a no-op
16
+ * and every gate salutes it.
18
17
  *
19
18
  * TWO SHAPES, both crisp, both scoped to CHECK-CLASS script names:
20
19
  *
21
20
  * A. ALWAYS-ZERO TAIL — the script's last command cannot fail (`… || true`,
22
- * `… || :`, `… || exit 0`, `…; exit 0`, a trailing `|| echo` fallback). A
21
+ * `… ||:`, `… || exit 0`, `…; exit 0`, a trailing `|| echo` fallback). A
23
22
  * shell script's status is its last command's status, so this is a proof, not
24
23
  * a heuristic: the script exits 0 no matter what the checker found.
25
24
  *
@@ -27,9 +26,8 @@
27
26
  * "Some line of the output does NOT match X" is never a checker's verdict; it
28
27
  * is true of virtually any non-empty output, including a wall of errors.
29
28
  *
30
- * Deliberately NOT flagged, because each is legitimate and FP-measured against the
31
- * real corpus (pi-task, aiz-server, aiz-client, gofer, mx5):
32
- * - `|| exit 1` — a HARDENING, the opposite of an escape (aiz-server).
29
+ * Deliberately NOT flagged, because each is legitimate:
30
+ * - `|| exit 1` a HARDENING, the opposite of an escape.
33
31
  * - any `||`/pipe in a NON-check script (`clean`, `dev`, `start`, `copy-fonts`) —
34
32
  * a teardown `rm -rf dist || true` is correct and common.
35
33
  * - pipes into formatters/reporters (`| tap-spec`, `| tee`) — those propagate
@@ -1,25 +1,24 @@
1
1
  /**
2
2
  * script-escape — deterministic detection of CHECK SCRIPTS THAT CANNOT FAIL.
3
3
  *
4
- * The failure this closes (mx5 run 13, PROMPT 4 item 4): the shipped package.json
5
- * declared, verbatim,
4
+ * The failure this closes: a package.json declaring
6
5
  * "lint": "prettier … && eslint --fix … && (tsc --noEmit 2>&1 | grep -qv 'TS18003' || true)"
7
6
  * The typecheck is neutered twice over — its output is piped into an INVERTED grep
8
7
  * (so the status becomes "some line did not match", never tsc's verdict), and the
9
8
  * whole group is closed with `|| true` (so the script exits 0 unconditionally). Every
10
9
  * consumer of that script — the repo-health verify gate, the final integration gate,
11
- * a human reading a green CI line — is reading a constant, not a measurement.
10
+ * a human reading a green CI line — is reading a constant, not a measurement. It
11
+ * looks fine for exactly as long as the checker happens to be clean.
12
12
  *
13
- * It was harmless in run 13 only by luck: tsc happened to be clean (validated). The
14
- * class is not harmless this is the same defect as run-8's F2 skip-escape, moved
15
- * one level out. findSkipEscapes (skip-escape.ts) scans a spec's own VERIFY block;
16
- * nothing scanned the SCRIPT DEFINITIONS those VERIFY blocks then invoke by name, so
17
- * `bun run lint` could be authored into a no-op and every gate would salute it.
13
+ * This is a skip-escape moved one level out. `findSkipEscapes` (skip-escape.ts)
14
+ * scans a spec's own VERIFY block; nothing else scans the SCRIPT DEFINITIONS those
15
+ * VERIFY blocks then invoke by name, so `bun run lint` can be authored into a no-op
16
+ * and every gate salutes it.
18
17
  *
19
18
  * TWO SHAPES, both crisp, both scoped to CHECK-CLASS script names:
20
19
  *
21
20
  * A. ALWAYS-ZERO TAIL — the script's last command cannot fail (`… || true`,
22
- * `… || :`, `… || exit 0`, `…; exit 0`, a trailing `|| echo` fallback). A
21
+ * `… ||:`, `… || exit 0`, `…; exit 0`, a trailing `|| echo` fallback). A
23
22
  * shell script's status is its last command's status, so this is a proof, not
24
23
  * a heuristic: the script exits 0 no matter what the checker found.
25
24
  *
@@ -27,9 +26,8 @@
27
26
  * "Some line of the output does NOT match X" is never a checker's verdict; it
28
27
  * is true of virtually any non-empty output, including a wall of errors.
29
28
  *
30
- * Deliberately NOT flagged, because each is legitimate and FP-measured against the
31
- * real corpus (pi-task, aiz-server, aiz-client, gofer, mx5):
32
- * - `|| exit 1` — a HARDENING, the opposite of an escape (aiz-server).
29
+ * Deliberately NOT flagged, because each is legitimate:
30
+ * - `|| exit 1` a HARDENING, the opposite of an escape.
33
31
  * - any `||`/pipe in a NON-check script (`clean`, `dev`, `start`, `copy-fonts`) —
34
32
  * a teardown `rm -rf dist || true` is correct and common.
35
33
  * - pipes into formatters/reporters (`| tap-spec`, `| tee`) — those propagate
@@ -57,7 +55,8 @@ const ALWAYS_ZERO_TAILS = [
57
55
  const INVERTED_GREP_RE = /\|\s*grep\s+(?:-\w*v\w*|-\w+\s+-\w*v\w*)/;
58
56
  /**
59
57
  * Strip trailing subshell/group closers and separators so the tail patterns see the
60
- * real last command. mx5's script ends `… || true)` without this the `)` hides it.
58
+ * real last command: a script ending `… && (tsc --noEmit || true)` hides its tail
59
+ * behind the `)`.
61
60
  */
62
61
  function tailOf(body) {
63
62
  let s = body.trim();
@@ -177,7 +176,7 @@ export function scriptEscapeVerifyFindings(findings) {
177
176
  export function scriptEscapeDefectText(findings) {
178
177
  return [
179
178
  'NEUTERED CHECK SCRIPT — this spec defines a check script that CANNOT FAIL, so every',
180
- 'gate that runs it reads a constant instead of a measurement (mx5 run 13 shipped',
179
+ 'gate that runs it reads a constant instead of a measurement (a run shipped',
181
180
  '`"lint": "… && (tsc --noEmit 2>&1 | grep -qv \'TS18003\' || true)"` — the typecheck',
182
181
  'was fully disarmed, and it went unnoticed only because tsc happened to be clean).',
183
182
  "Rewrite each so it PROPAGATES the checker's exit status: drop the `|| true` /",
@@ -20,7 +20,7 @@ export declare function findBindEvidence(src: string, appNames?: Set<string>): s
20
20
  /** Why this file makes the project a SERVING one, or null. */
21
21
  export declare function findServeExpectation(src: string): string | null;
22
22
  /** A design/spec clause that names a served path — the plan-side half of the same
23
- * expectation (mx5's `DESIGN/PROJECT.md:285`: "serves `/api` + static `dist/`"). */
23
+ * expectation, e.g. a design that says "serves `/api` + static `dist/`". */
24
24
  export declare function planExpectsServing(planText: string | undefined): string | null;
25
25
  /** The platform that would bind on this project's behalf, or null. */
26
26
  export declare function opaqueLauncher(cwd: string): string | null;