@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
@@ -23,13 +23,12 @@ export const MAX_AUTO_AUTOFIX = 3;
23
23
  /**
24
24
  * The reason an auto-ACCEPT is being written — NAMED, not assumed.
25
25
  *
26
- * The line this replaces asserted "autofix budget spent" on every branch. It was
27
- * measured false in 2709 of 2709 recorded accepts (scripts/yolo-accept-baserate.ts):
28
- * the budget has never once reached MAX_AUTO_AUTOFIX anywhere in the corpus — 30%
29
- * of accepts are UNOBSERVED (the research is never even consulted) and 70% are an
30
- * ACCEPT recommendation with the budget fully untouched. A durable trail that
31
- * misstates why a defect shipped is worse than no trail: mx5 run 19's TASK_0009
32
- * reads as an exhausted fixer when nothing was ever attempted.
26
+ * Four disjoint branches reach the terminal auto-ACCEPT, and only ONE of them has
27
+ * spent the autofix budget. An UNOBSERVED FAIL never consults the research; a
28
+ * frozen-blocked one is a contradiction no re-run can resolve; an ACCEPT
29
+ * recommendation can arrive with the budget untouched. A single "autofix budget
30
+ * spent" line would be false on three of the four, and a durable trail that
31
+ * misstates why a defect shipped reads as an exhausted fixer that never tried.
33
32
  */
34
33
  export function yoloAcceptReason(c) {
35
34
  if (c.isUnobserved)
@@ -44,7 +43,7 @@ export function yoloAcceptReason(c) {
44
43
  : `judge recommended ACCEPT (autofix budget ${c.autoFixCount}/${MAX_AUTO_AUTOFIX} already spent)`;
45
44
  }
46
45
  // The trail-side output ceiling lives in clamp-output.ts, so the render probe's
47
- // evidence clamps identically (nexttask 19B) — one ceiling, one implementation.
46
+ // evidence clamps identically — one ceiling, one implementation.
48
47
  /**
49
48
  * Show the boxed two-choice picker after a verify FAIL and return what the user
50
49
  * decided. The model-recommended card is placed first so the renderer tints it
@@ -73,12 +72,10 @@ export async function askVerifyResolution(ctx, title, failReason, rec) {
73
72
  * recommendation → unattended autofix → picker) until it verifies, is accepted,
74
73
  * or terminates.
75
74
  *
76
- * Split out of `runGatesForTask` at the single boolean that crosses to the
77
- * ENFORCE half. It carries 8 mutable locals over ~290 lines and has four terminal
78
- * exits; enforce carries one local and always falls through. Joining them meant a
79
- * test of the enforce differential had to traverse this whole loop first, which is
80
- * why `deps.verify` was driven by an invocation counter whose first return existed
81
- * only to unlock `mode === 'edit'`.
75
+ * Split from `runGatesForTask` at the single boolean that crosses to the ENFORCE
76
+ * half (`cleanPass`). This loop has four terminal exits and carries the whole
77
+ * negotiation; enforce always falls through. Joined, a test of the enforce
78
+ * differential would have to traverse this entire loop first.
82
79
  */
83
80
  export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
84
81
  let active = ctxIn;
@@ -107,9 +104,9 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
107
104
  let autoFixCount = 0;
108
105
  // Set when the bounded lint-fix reports the `frozen-path:` rejection: the
109
106
  // repo-health FAIL can only be fixed by editing a path THIS task's spec
110
- // froze (mx5 run 12's unsatisfiable registration pair). An impl re-run is
111
- // under the same freeze rule 4b fails the task if it complies with the
112
- // linter — so unattended AUTOFIX rounds CANNOT converge and are skipped;
107
+ // froze. An impl re-run happens under the same freeze rule 4b fails the
108
+ // task if it complies with the linter so unattended AUTOFIX rounds
109
+ // CANNOT converge and are skipped;
113
110
  // the picker is forced with the cross-task contradiction named, and the
114
111
  // defect is recorded as a durable debt for the final gate.
115
112
  let frozenContradiction = null;
@@ -202,10 +199,9 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
202
199
  // AUTOFIX must not bootstrap the full budget from it.
203
200
  && !yoloRescueUsed;
204
201
  // YOLO, THE RESCUE BRANCH: the recommendation is ACCEPT, nobody can be
205
- // asked, and the unattended budget is UNTOUCHED. Accepting here ships a
206
- // defect having attempted nothing mx5 run 19 did exactly that twice,
207
- // and the final-gate autofix later fixed one of the two in a single pass
208
- // (`0 pass 130 fail` → `121 pass 0 fail`). So spend ONE attempt first.
202
+ // asked, and the unattended budget is UNTOUCHED. Accepting here would
203
+ // ship a defect having attempted nothing, on a judgement the human who
204
+ // would normally weigh it never saw. So spend ONE attempt first.
209
205
  // Bounded by construction: one, not MAX_AUTO_AUTOFIX, so an ACCEPT
210
206
  // recommendation can never restart a full loop; if it still FAILs the
211
207
  // next turn falls through to the same auto-ACCEPT and the same debt.
@@ -226,8 +222,8 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
226
222
  if (yoloChoice !== null) {
227
223
  choice = yoloChoice;
228
224
  // NAME the branch. This line is the durable record of why a defect
229
- // shipped; asserting a spent budget on all four branches made run
230
- // 19's zero-attempt accepts read as an exhausted fixer.
225
+ // shipped, and three of the four branches that reach it have spent
226
+ // no budget at all.
231
227
  await rec(`resolution: auto-ACCEPTED despite verify FAIL — ${yoloAcceptReason({
232
228
  isUnobserved,
233
229
  isFrozenBlocked,
@@ -258,9 +254,9 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
258
254
  if (!byYolo)
259
255
  await rec('resolution: user ACCEPTED the work despite verify FAIL');
260
256
  // Durable debt: the human blessed a FAILing artifact as-is, so the
261
- // defect ships and nothing else revisits it (mx5 run 4 B3 / run 8
262
- // TASK_0012). Record it to the run ledger; the final integration gate
263
- // re-checks it at run end and surfaces it if still open. Best-effort.
257
+ // defect ships and nothing else in this task revisits it. Record it
258
+ // to the run ledger; the final integration gate re-checks it at run
259
+ // end and surfaces it if still open. Best-effort.
264
260
  // The frozen-blocked routing above already recorded this defect (with
265
261
  // the contradiction named) — don't double-enter it in the ledger.
266
262
  if (!frozenDebtRecorded) {
@@ -275,13 +271,12 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
275
271
  }
276
272
  }
277
273
  // ROOT CAUSE: an accepted FAIL that some OTHER task's file caused is
278
- // not fixed by accepting it — run 14 accepted this shape repeatedly
279
- // and the causing bug outlived the whole run. Queue the scoped repair
280
- // so the plan actually closes it.
274
+ // not fixed by accepting it — every later task keeps tripping over
275
+ // the same bug. Queue the scoped repair so the plan closes it.
281
276
  await routeRootCause(failReason, recOutcome.rationale, 'worktree');
282
277
  // Cross-task deletions the verify probe detected ship in the next
283
278
  // commit with this ACCEPT — record each as its own durable debt so
284
- // the final gate re-checks them (mx5 run 12 PROMPT 2). Best-effort.
279
+ // the final gate re-checks them. Best-effort.
285
280
  for (const del of verified.crossTaskDeletions ?? []) {
286
281
  try {
287
282
  await deps.recordDebt?.(p.cwd, p.taskId, crossTaskDeletionReason(del), 'cross-task-deletion');
@@ -296,11 +291,10 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
296
291
  }
297
292
  // AUTOFIX: re-run the implementation turn with the failure (and any typed
298
293
  // guidance) prepended as a RE-ATTEMPT banner, then re-verify. The
299
- // recommendation child already LOCATED the defect while deciding (mx5
300
- // run 6: it pinned the exact SQL alias bug and the afterAll DB-drop)
301
- // hand that diagnosis to the re-run so it fixes the located cause
302
- // instead of re-deriving it from the bare FAIL line. Skipped when there
303
- // is no researched rationale beyond the failure text itself.
294
+ // recommendation child already LOCATED the defect while deciding, so
295
+ // hand its diagnosis to the re-run rather than making it re-derive the
296
+ // cause from the bare FAIL line. Skipped when there is no researched
297
+ // rationale beyond the failure text itself.
304
298
  // Only the picker branch may claim a person chose this: the two
305
299
  // unattended branches already recorded themselves one line above, and a
306
300
  // trail that says "user chose" when nobody was asked is the same lie the
@@ -362,8 +356,7 @@ export async function resolveVerifyGate(ctxIn, deps, p, rec, routeRootCause) {
362
356
  * then decide whether the pass\'s own commit survives.
363
357
  *
364
358
  * `reVerify` is deliberately NOT `deps.verify`. They answer two different
365
- * questions — the gate above, and this differential — and while they shared one
366
- * field the only way to answer them differently was to count invocations.
359
+ * questions — the gate above, and this differential — so they are two fields.
367
360
  *
368
361
  * Reads `active` and never reassigns it: nothing here can replace the live
369
362
  * session, unlike the autofix in the verify half.
@@ -378,19 +371,19 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
378
371
  const mode = verifyCleanPass ? 'edit' : 'flag';
379
372
  // BASELINE repo health, captured BEFORE the edit pass touches the tree, so the
380
373
  // pre-commit gate below is DIFFERENTIAL: it can tell an enforce-CAUSED
381
- // regression (was clean, now fails) from a repo that was ALREADY unhealthy
382
- // (run-8 F8: five enforce passes were discarded on a lint that was already
383
- // crashing exit 2 before enforce edited anything; the discard threw away
384
- // good work for a fault it did not cause). Only meaningful in edit mode (flag
374
+ // regression (was clean, now fails) from a repo that was ALREADY unhealthy.
375
+ // Without the baseline, a lint that was already crashing before the pass ran
376
+ // makes every enforce pass look like the cause, and its good work is
377
+ // discarded for a fault it did not commit. Only meaningful in edit mode (flag
385
378
  // makes no edits); the task's work is already committed so this reflects the
386
379
  // committed state the pass is about to build on.
387
380
  const healthBefore = mode === 'edit' && deps.repoHealth ?
388
381
  await deps.repoHealth(active, p.cwd, p.title)
389
382
  : undefined;
390
383
  const verdict = await deps.enforce(active, p.cwd, p.title, mode);
391
- // FROZEN-PATH WRITE-DENY (mechanical, not prompt — the "MUST NOT edit"
392
- // instruction is A/B-proven ~0–1/5 reliable on the weak model): the enforce
393
- // EDIT pass runs read,edit and has been seen mutating a path the spec froze.
384
+ // FROZEN-PATH WRITE-DENY (mechanical, not prompt — a "MUST NOT edit"
385
+ // instruction is not reliable): the enforce EDIT pass runs read,edit and can
386
+ // mutate a path the spec froze.
394
387
  // Before its edits are inspected/committed below, restore any frozen path it
395
388
  // touched to the committed task state, so the violating write cannot land in
396
389
  // the ENFORCE GUIDELINES commit regardless of what the model intended. The
@@ -417,20 +410,21 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
417
410
  if (!verdict.ok) {
418
411
  active.ui.notify(`${p.tag}: guideline ${mode === 'edit' ? 'enforcement' : 'review'} on "${p.title}" — ${verdict.reason ?? 'not clean'} — continuing.`, 'warning');
419
412
  }
420
- // PRE-COMMIT health gate on enforce edits: live data shows the edit pass broke
421
- // the repo's own lint in 11/16 tasks, each burning a commit + model re-verify +
422
- // revert cycle. A deterministic static check BEFORE committing skips the cycle
423
- // and discards the bad edits outright. Only runs when the tree is actually
413
+ // PRE-COMMIT health gate on enforce edits: an edit pass that breaks the
414
+ // repo's own lint would otherwise burn a commit, a model re-verify and a
415
+ // revert before anything noticed. A deterministic static check BEFORE
416
+ // committing skips that cycle and discards the bad edits outright. Only runs
417
+ // when the tree is actually
424
418
  // dirty (or dirtiness is unknowable); the differential guard below still
425
419
  // catches behavioral regressions the static check cannot see.
426
420
  //
427
- // The gate is DIFFERENTIAL, not absolute (run-8 F8): discard the edits only
421
+ // The gate is DIFFERENTIAL, not absolute: discard the edits only
428
422
  // when they REGRESSED the health signal — clean before, failing after. A repo
429
423
  // that was already failing before enforce ran is not enforce's fault, so its
430
- // edits are KEPT (and the pre-existing failure is recorded, to be caught by the
431
- // final integration gate, not blamed on this pass). The failing command's
432
- // output is captured into the trail so the discard is explainable F8 was
433
- // unreproducible precisely because only the exit code was recorded.
424
+ // edits are KEPT (and the pre-existing failure is recorded, to be caught by
425
+ // the final integration gate, not blamed on this pass). The failing command's
426
+ // OUTPUT is captured into the trail, not just its exit code, so the discard
427
+ // is explainable after the run.
434
428
  let enforceEditsBlocked = false;
435
429
  if (mode === 'edit' && deps.repoHealth && editsMade !== false) {
436
430
  const after = await deps.repoHealth(active, p.cwd, p.title);
@@ -460,16 +454,15 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
460
454
  }
461
455
  }
462
456
  if (mode === 'edit' && !enforceEditsBlocked && editsMade === false) {
463
- // KNOWN-clean tree (dirty dep ran and found no code edits): skip the
457
+ // KNOWN-clean tree (the dirty dep ran and found no code edits): skip the
464
458
  // enforce commit AND the differential re-verify outright. Without this
465
459
  // gate the commit is never empty — the .pi-tasks gate-trail lines written
466
- // above make it real — so mx5 run 5 burned a full model re-verify on an
467
- // UNCHANGED tree for all ~29 tasks, and the 10 FAILs those re-verifies
468
- // produced (all real, pre-existing defects) were "reverted" into the
469
- // void: the revert dropped a bookkeeping-only commit and the defect
470
- // reports were discarded while the tasks stayed PASS. No edits nothing
471
- // to guard no commit, no re-verify, no revert. The trail lines ride
472
- // along in the next ordinary commit.
460
+ // above make it real — so every task would burn a model re-verify on an
461
+ // UNCHANGED tree, and any FAIL it turned up would be "reverted" into the
462
+ // void: the revert drops a bookkeeping-only commit while the defect
463
+ // report is discarded and the task stays PASS. No edits nothing to
464
+ // guard no commit, no re-verify, no revert. The trail lines ride along
465
+ // in the next ordinary commit.
473
466
  await rec('enforce(edit): no code edits — enforce commit and re-verify skipped');
474
467
  }
475
468
  else if (mode === 'edit' && !enforceEditsBlocked) {
@@ -484,30 +477,27 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
484
477
  await differential(active, p.cwd, p.title, p.taskId)
485
478
  : { ok: true };
486
479
  const afterReason = after.reason ?? 'enforce re-verify failed';
487
- // PRE-EXISTING-CAUSE KEEP PATH (mx5 run 14 item 5b). Both of run
488
- // 14's enforce-reverts were this shape: the re-verify FAILed on
489
- // TASK_0007's `test/teardown.ts` TRUNCATE bug a file the enforce
490
- // pass never touched and the differential
491
- // reverted enforce's edits anyway, destroying good work over a fault
492
- // it did not cause AND leaving the actual cause unscheduled. When the
493
- // FAIL is attributed to another task's untouched file, KEEP the edits
494
- // and route the real defect to a repair task instead. Everything
495
- // unknown (git unavailable, no provenance, this task touched the file,
496
- // an environment-blamed FAIL) falls through to the revert below —
497
- // the conservative pre-existing behavior.
480
+ // PRE-EXISTING-CAUSE KEEP PATH. A re-verify can FAIL on a file the
481
+ // enforce pass never touched another task's bug this one merely
482
+ // ran into. Reverting there destroys good work over a fault it did
483
+ // not cause AND leaves the actual cause unscheduled. So when the FAIL
484
+ // is attributed to another task's untouched file, KEEP the edits and
485
+ // route the real defect to a repair task. Everything unknown (git
486
+ // unavailable, no provenance, this task touched the file, an
487
+ // environment-blamed FAIL) falls through to the revert below.
498
488
  //
499
- // The scope is `enforce-commit`, NOT the task's own commit (mx5 run
500
- // 18 / nexttask 4). This differential decides whether to discard the
501
- // ENFORCE COMMIT, so the causal question is "could the enforce diff
502
- // have caused this?" — asking what the TASK touched answers a
503
- // question nobody at this seam is asking, and in run 18 it answered
504
- // it in a way that destroyed a correct one-line change.
489
+ // The scope is `enforce-commit`, NOT the task's own commit. This
490
+ // differential decides whether to discard the ENFORCE COMMIT, so the
491
+ // causal question is "could the enforce diff have caused this?".
492
+ // Asking what the TASK touched answers a question nobody at this seam
493
+ // is asking.
505
494
  const rootCause = after.ok ? null : await routeRootCause(afterReason, '', 'enforce-commit');
506
- // ATTRIBUTION PRE-FILTER (mx5 run 18 / nexttask 4). The root-cause
507
- // channel above needs a blame CUE, a path-separator token and known
508
- // provenance; run 18's FAIL text carried none of the three (it named
509
- // a bare `MyListings.spec.tsx:186`), so it fell straight through to
510
- // the revert. This filter asks only the mechanical question: does the
495
+ // ATTRIBUTION PRE-FILTER. The root-cause channel above needs a blame
496
+ // CUE, a path-separator token and known provenance. A FAIL text can
497
+ // carry none of the three a bare `MyListings.spec.tsx:186` names a
498
+ // file with no directory and no blame wording — and then falls
499
+ // straight through to the revert. This filter asks only the
500
+ // mechanical question: does the
511
501
  // failing check name any file the ENFORCE COMMIT touched? Disjoint =>
512
502
  // the revert cannot repair the failure, so keep the edits and route
513
503
  // the defect. Unknown diff, or a FAIL naming no file at all, still
@@ -527,15 +517,14 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
527
517
  else if (!after.ok && attribution?.verdict === 'keep') {
528
518
  // KEEP, mechanically justified: every file the failing check named
529
519
  // is outside the enforce diff (and outside its companions — a
530
- // touched file's own spec/story counts as inside). Discarding the
531
- // enforce commit could not repair this, and in run 18 doing so
532
- // cost a correct change that the final gate then re-made.
520
+ // touched file's own spec/story counts as inside). Discarding
521
+ // the enforce commit could not repair this, so doing so only
522
+ // costs the correct change the pass made.
533
523
  await rec(`enforce: re-verify FAILED (${afterReason.slice(0, 200)}) but the failing check names only \`${attribution.named.join(', ')}\`, `
534
524
  + `which the ENFORCE COMMIT does not touch (its diff: ${attribution.enforceDiff.join(', ') || '—'}) — `
535
525
  + 'reverting it could not repair this, so the edits are KEPT and the defect is recorded as durable debt');
536
- // Keeping the edits must NOT lose the finding that was mx5 run
537
- // 5's mistake. Same durability as the revert path; only the
538
- // disposition of the edits differs.
526
+ // Keeping the edits must NOT lose the finding. Same durability
527
+ // as the revert path; only the disposition of the edits differs.
539
528
  await deps.recordDebt?.(p.cwd, p.taskId, afterReason, 'enforce-kept');
540
529
  // …and, when the named file is somebody else's committed work,
541
530
  // queue the scoped repair so something actually FIXES it.
@@ -571,12 +560,12 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
571
560
  ` — the check names \`${attribution.overlap.named}\`, the enforce diff touches \`${attribution.overlap.enforce}\``
572
561
  : ''}]`
573
562
  : ''));
574
- // Persist the FAIL as a durable defect (mx5 run 10 item 3). The
575
- // revert restores the tree the ORIGINAL verify already blessed, so
576
- // this re-verify caught a defect that verify's earlier PASS missed —
577
- // erasing it with the enforce edits buried the terminal fault 8.5h
578
- // before run end. The final gate re-checks/surfaces it (static-class
579
- // auto-closes if a later task fixed the statics; else stays open).
563
+ // Persist the FAIL as a durable defect. The revert restores the
564
+ // tree the ORIGINAL verify already blessed, so this re-verify
565
+ // caught something that verify's earlier PASS missed — erasing it
566
+ // along with the enforce edits would bury a real fault. The final
567
+ // gate re-checks and surfaces it (static-class auto-closes if a
568
+ // later task fixed the statics; otherwise it stays open).
580
569
  await deps.recordDebt?.(p.cwd, p.taskId, after.reason ?? 'enforce re-verify failed', 'enforce-revert');
581
570
  active.ui.notify(`${p.tag}: guideline fixes regressed verification on "${p.title}" (${(after.reason ?? 'now fails').slice(0, 120)}) — ${deps.revert ? 'reverted them, kept the verified work' : 'left in place (no revert available)'}.`, 'warning');
582
571
  }
@@ -593,7 +582,7 @@ export async function runEnforcePass(active, deps, p, rec, routeRootCause, args)
593
582
  }
594
583
  else if (deps.enforce) {
595
584
  // deps.enforce wired but nothing was committed this round — record the skip
596
- // so a missing enforce run is explainable from the trail (mx5 audit gap).
585
+ // so a missing enforce run is explainable from the trail.
597
586
  await rec('enforce: skipped (nothing committed this round)');
598
587
  }
599
588
  }
@@ -607,12 +596,12 @@ export async function runGatesForTask(ctxIn, deps, p) {
607
596
  }
608
597
  };
609
598
  /**
610
- * ROOT-CAUSE CHANNEL (mx5 run 14 item 5). Ask whether a FAIL was caused by a
599
+ * ROOT-CAUSE CHANNEL. Ask whether a FAIL was caused by a
611
600
  * pre-existing defect in a file some OTHER task created and this task never
612
601
  * touched. On a hit: record the durable debt (so the final gate surfaces it)
613
- * and queue a scoped repair task (so something finally FIXES it run 14
614
- * recorded the same `test/teardown.ts` cause twice and scheduled nothing, and
615
- * the bug survived ~24h). Returns the candidate so the caller can also decide
602
+ * and queue a scoped repair task, so something finally FIXES it rather than
603
+ * recording the same cause once per task that trips over it. Returns the
604
+ * candidate so the caller can also decide
616
605
  * NOT to punish the current task for it. Never throws: any fault degrades to
617
606
  * null, i.e. exactly the pre-existing behavior.
618
607
  */
@@ -652,10 +641,10 @@ export async function runGatesForTask(ctxIn, deps, p) {
652
641
  if (commit.committed) {
653
642
  await rec(`commit: task snapshot committed${commit.note ? ` (${commit.note})` : ''}`);
654
643
  // SAY WHAT WAS LEFT OUT. The stage skips untracked regenerable test-runner
655
- // output (mx5 run 20: TASK_0027's `git add -A` swept in three Playwright
656
- // failure screenshots and two later fix attempts were rejected for deleting
657
- // them). A SILENT exclusion is the same failure class as the silent
658
- // ignored-path write nexttask 4 closed, so it gets its own trail line.
644
+ // output, so a blind `git add -A` cannot make a screenshot a tracked
645
+ // deliverable that a later fix attempt is then rejected for deleting. A
646
+ // SILENT exclusion is the same failure class as a silent ignored-path write,
647
+ // so it gets its own trail line.
659
648
  if (commit.excluded && commit.excluded.length > 0) {
660
649
  await rec(`commit: left ${commit.excluded.length} untracked test-runner artifact(s) out of the `
661
650
  + `snapshot — regenerable output, not deliverables: `
@@ -668,8 +657,8 @@ export async function runGatesForTask(ctxIn, deps, p) {
668
657
  await rec(`commit: skipped (${commit.reason ?? 'unknown'})`);
669
658
  // A benign skip ("nothing to commit", auto-commit off) is a warning. A real
670
659
  // git failure is louder: it silently disables enforce AND every commit-based
671
- // guard mx5 run 4 lost all 10 commits (no container git identity) with only
672
- // per-task warnings to show for it. "blocked" is the unmerged-index refusal
660
+ // guard, so a whole run can end with nothing committed and only per-task
661
+ // warnings to show for it. "blocked" is the unmerged-index refusal
673
662
  // (gitCommitAll) — the same severity: nothing can commit until it's resolved.
674
663
  const gitFailure = /^git (commit|add) (failed|blocked)/.test(commit.reason ?? '');
675
664
  active.ui.notify(gitFailure ?
@@ -24,10 +24,10 @@ export declare function setTaskSection(cwd: string, id: string, heading: string,
24
24
  /**
25
25
  * Append one timestamped line to the task's `## gates` section — the durable
26
26
  * per-task trail of gate outcomes (verify verdicts, enforce mode/verdict, commit
27
- * results). Motivated by the mx5 audit: verdict text lived only in memory and
28
- * terminal notifies, so "did enforce run for this task, and in which mode?" was
29
- * unanswerable from artifacts. Best-effort by design: a failure to record must
30
- * never break the gate sequence, so all errors are swallowed.
27
+ * results). A verdict that lives only in memory and a terminal notify cannot
28
+ * answer "did enforce run for this task, and in which mode?" after the run.
29
+ * Best-effort by design: a failure to record must never break the gate sequence,
30
+ * so all errors are swallowed.
31
31
  */
32
32
  export declare function appendGateRecord(cwd: string, id: string, line: string): Promise<void>;
33
33
  /** Remove a section (heading + body) if present; a no-op when it's absent. */
@@ -115,10 +115,10 @@ export async function setTaskSection(cwd, id, heading, content) {
115
115
  /**
116
116
  * Append one timestamped line to the task's `## gates` section — the durable
117
117
  * per-task trail of gate outcomes (verify verdicts, enforce mode/verdict, commit
118
- * results). Motivated by the mx5 audit: verdict text lived only in memory and
119
- * terminal notifies, so "did enforce run for this task, and in which mode?" was
120
- * unanswerable from artifacts. Best-effort by design: a failure to record must
121
- * never break the gate sequence, so all errors are swallowed.
118
+ * results). A verdict that lives only in memory and a terminal notify cannot
119
+ * answer "did enforce run for this task, and in which mode?" after the run.
120
+ * Best-effort by design: a failure to record must never break the gate sequence,
121
+ * so all errors are swallowed.
122
122
  */
123
123
  export async function appendGateRecord(cwd, id, line) {
124
124
  try {
@@ -33,9 +33,10 @@ export function emitFrontMatter(fm) {
33
33
  }
34
34
  export function parseFrontMatter(content) {
35
35
  // `\r?\n` throughout so a CRLF/Windows task file parses too. Reads normally
36
- // pass through readTextFile (which normalizes to LF), but tolerating CRLF at
37
- // the parser itself is the safety net for any read site that forgets to —
38
- // exactly the class of miss that shipped in 0.17.8. See shared/fs-text.ts.
36
+ // pass through readTextFile (which normalizes CRLF and lone CR to LF), but
37
+ // tolerating CRLF at the parser itself is the safety net for any read site
38
+ // that forgets to. A lone CR still needs the normalization — no regex here
39
+ // matches it, because every one requires an `\n`. See shared/fs-text.ts.
39
40
  const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(content);
40
41
  if (!m)
41
42
  return null;
@@ -39,7 +39,7 @@ export interface CrossTaskDeletion {
39
39
  export declare function findCrossTaskDeletions(changes: TreeChangeSummary, currentTaskId: string, introducedBy: (rel: string) => string | null | Promise<string | null>): Promise<CrossTaskDeletion[]>;
40
40
  /**
41
41
  * Finding lines for the verify prompt (the substitution-probe injection pattern:
42
- * the rule alone is A/B-proven ignored, the rule plus a concrete finding naming
43
- * the file is what lands).
42
+ * a rule on its own is ignored; the rule plus a concrete finding naming the
43
+ * file is what lands).
44
44
  */
45
45
  export declare function crossTaskDeletionVerifyFindings(found: CrossTaskDeletion[]): string[];
@@ -1,23 +1,21 @@
1
1
  /**
2
2
  * task-provenance — file → introducing-task lookup, and the cross-task deletion
3
- * detection built on it (mx5 run-12 PROMPT 2).
3
+ * detection built on it.
4
4
  *
5
- * The failure class: write-enabled gate/fix children can DELETE a sibling task's
6
- * committed deliverable to turn a red check green. Live (mx5 2026-07-16,
7
- * verify-debug.log 17:53:44): `bun run lint` was permanently red because
8
- * committed playwright ct files were not registered in a spec-frozen tsconfig —
9
- * so a lint-fix child deleted TASK_0020's verified deliverables and the pass
10
- * returned ok. Every existing guard missed the class:
5
+ * The failure class: a write-enabled gate or fix child can DELETE a sibling
6
+ * task's committed deliverable to turn a red check green a lint that only goes
7
+ * green once the offending files are gone. Every other guard misses that class:
11
8
  *
12
9
  * - the lint-fix revert-guard checks only pre-DIRTY files reverted and
13
- * pre-UNTRACKED files gone — a CLEAN tracked sibling file is in neither set;
10
+ * pre-UNTRACKED files gone — a CLEAN tracked sibling file is in neither set
11
+ * (lint-fix.ts says so at its own site);
14
12
  * - the frozen-path guard covers only paths THIS task's spec froze;
15
- * - the run-11 deletion guard covers only the final-fix child;
13
+ * - `findForbiddenDeletions` (write-guard.ts) was reached only by the final-fix
14
+ * child (final-gate-fix.ts);
16
15
  * - the impl turn legitimately deletes files (its own refactors), so a blanket
17
16
  * ban is wrong — the discriminator must be PROVENANCE: who introduced the file.
18
17
  *
19
- * The provenance primitive (extracted from final-gate.ts, where it fed the
20
- * conflicting-claim annotation) resolves a file to the task whose commit ADDED it
18
+ * The provenance primitive resolves a file to the task whose commit ADDED it
21
19
  * (`git log --diff-filter=A` → oldest task-subject commit). Every unknown — a file
22
20
  * predating the run, a non-task commit, any git error — degrades to null:
23
21
  * inconclusive is never evidence, so the guards built on this may only cost time,
@@ -90,8 +88,8 @@ export async function findCrossTaskDeletions(changes, currentTaskId, introducedB
90
88
  }
91
89
  /**
92
90
  * Finding lines for the verify prompt (the substitution-probe injection pattern:
93
- * the rule alone is A/B-proven ignored, the rule plus a concrete finding naming
94
- * the file is what lands).
91
+ * a rule on its own is ignored; the rule plus a concrete finding naming the
92
+ * file is what lands).
95
93
  */
96
94
  export function crossTaskDeletionVerifyFindings(found) {
97
95
  return found.map(f => `\`${f.path}\` — introduced and committed by ${f.owner}, DELETED by this task's work`);
@@ -15,9 +15,10 @@ export interface TaskFrontMatter {
15
15
  title: string;
16
16
  /**
17
17
  * Short, human-readable display label compressed from `title`. Visual only —
18
- * `title` always holds the full text the pipeline reads. Absent on pre-label
19
- * tasks and on small tasks whose title is already short; display falls back to
20
- * a deterministic truncation of `title` when missing. See title-label.ts.
18
+ * `title` always holds the full text the pipeline reads. Absent on a task file
19
+ * written before the field existed, and on any task that never reached the
20
+ * phase that writes it; display then falls back to a deterministic truncation
21
+ * of `title` (`titleForDisplay`, parsers.ts). See title-label.ts.
21
22
  */
22
23
  label?: string;
23
24
  reason?: string;
@@ -2,19 +2,17 @@
2
2
  * What each way a gated task can END means for persistence and for what the user
3
3
  * is told — stated once, for both `/task` and `/task-auto`.
4
4
  *
5
- * `runGatesForTask` is already shared verbatim by the two commands, so both
6
- * consume the same five-kind outcome. What was NOT shared was the answer to the
7
- * three questions each kind raises: demote the inner task file? fail the parent
8
- * run file? what do we say, at what level? Those were two `switch`es 600 lines
9
- * apart with line-for-line correspondencedown to the identical
10
- * `reason.slice(0, 160)` truncation differing only in the resume verb, whether
11
- * a parent run file exists to fail, and whether the message names the step.
5
+ * `runGatesForTask` is shared by the two commands, so both consume the same
6
+ * outcome union. Each kind raises the same three questions: demote the inner task
7
+ * file? fail the parent run file? what do we say, at what level? This table
8
+ * answers them once. It touches none of either loop's state it is the
9
+ * outcome (persistence, message) mapping and nothing else and a new outcome
10
+ * kind is a compile error here rather than a silent fallthrough in whichever
11
+ * command was not updated.
12
12
  *
13
- * That is not the shared "task-runner base" CONTEXT.md rules out, and for the
14
- * same reason `runFinalGateStage` could leave `runAutoLoop`: this is the
15
- * outcome → (persistence, message) mapping, and it touches none of either loop's
16
- * state. Adding a sixth outcome kind is now a compile error in one place rather
17
- * than a silently-unhandled fallthrough in whichever command was not updated.
13
+ * The three things that genuinely differ per command are parameters: the resume
14
+ * verb, whether the message names a step (`/task` runs one task and passes an
15
+ * empty `at`), and whether a parent run file exists to fail.
18
16
  *
19
17
  * The PRE-gate outcomes (`res.sessionCancelled` / `res.interrupted` / `!res.ok`,
20
18
  * before the gate runs) are deliberately NOT here. Their wording genuinely
@@ -28,7 +26,7 @@
28
26
  export type TerminalOutcomeKind = 'done' | 'paused' | 'session-cancelled' | 'cancelled' | 'interrupted' | 'failed';
29
27
  /** What the message needs to name. */
30
28
  export interface TerminalMessageContext {
31
- /** The task or run id shown to the user (`TASK_0007`, `AUTO_0002`, `Task`). */
29
+ /** The task or run id shown to the user. */
32
30
  tag: string;
33
31
  /**
34
32
  * The step this happened at, ALREADY formatted with its leading space —
@@ -45,7 +43,7 @@ export interface TerminalOutcome {
45
43
  /**
46
44
  * Demote the INNER task file to resumable. It reads `completed` from
47
45
  * spec-handoff, and leaving it that way is how a failed run's task file
48
- * claimed success in the run 6 audit.
46
+ * claimed success after the run had failed.
49
47
  */
50
48
  markResumable: boolean;
51
49
  /**
@@ -58,8 +56,8 @@ export interface TerminalOutcome {
58
56
  message: (c: TerminalMessageContext) => string;
59
57
  }
60
58
  /**
61
- * Truncate a failure reason for a one-line notification, or produce nothing.
62
- * Both commands did this identically and inline.
59
+ * Truncate a failure reason for a one-line notification, or produce nothing when
60
+ * there is no reason.
63
61
  */
64
62
  export declare function formatWhy(reason?: string): string;
65
63
  /** The step suffix, or empty when the command has no step to name. */
@@ -2,19 +2,17 @@
2
2
  * What each way a gated task can END means for persistence and for what the user
3
3
  * is told — stated once, for both `/task` and `/task-auto`.
4
4
  *
5
- * `runGatesForTask` is already shared verbatim by the two commands, so both
6
- * consume the same five-kind outcome. What was NOT shared was the answer to the
7
- * three questions each kind raises: demote the inner task file? fail the parent
8
- * run file? what do we say, at what level? Those were two `switch`es 600 lines
9
- * apart with line-for-line correspondencedown to the identical
10
- * `reason.slice(0, 160)` truncation differing only in the resume verb, whether
11
- * a parent run file exists to fail, and whether the message names the step.
5
+ * `runGatesForTask` is shared by the two commands, so both consume the same
6
+ * outcome union. Each kind raises the same three questions: demote the inner task
7
+ * file? fail the parent run file? what do we say, at what level? This table
8
+ * answers them once. It touches none of either loop's state it is the
9
+ * outcome (persistence, message) mapping and nothing else and a new outcome
10
+ * kind is a compile error here rather than a silent fallthrough in whichever
11
+ * command was not updated.
12
12
  *
13
- * That is not the shared "task-runner base" CONTEXT.md rules out, and for the
14
- * same reason `runFinalGateStage` could leave `runAutoLoop`: this is the
15
- * outcome → (persistence, message) mapping, and it touches none of either loop's
16
- * state. Adding a sixth outcome kind is now a compile error in one place rather
17
- * than a silently-unhandled fallthrough in whichever command was not updated.
13
+ * The three things that genuinely differ per command are parameters: the resume
14
+ * verb, whether the message names a step (`/task` runs one task and passes an
15
+ * empty `at`), and whether a parent run file exists to fail.
18
16
  *
19
17
  * The PRE-gate outcomes (`res.sessionCancelled` / `res.interrupted` / `!res.ok`,
20
18
  * before the gate runs) are deliberately NOT here. Their wording genuinely
@@ -25,8 +23,8 @@
25
23
  * overrides, which is not a simplification.
26
24
  */
27
25
  /**
28
- * Truncate a failure reason for a one-line notification, or produce nothing.
29
- * Both commands did this identically and inline.
26
+ * Truncate a failure reason for a one-line notification, or produce nothing when
27
+ * there is no reason.
30
28
  */
31
29
  export function formatWhy(reason) {
32
30
  return reason ? ` — ${reason.slice(0, 160)}` : '';