@mjasnikovs/pi-task 0.38.29 → 0.38.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/dist/config/config.d.ts +70 -70
  2. package/dist/config/config.js +26 -35
  3. package/dist/config/extension-list.d.ts +6 -5
  4. package/dist/config/extension-list.js +3 -2
  5. package/dist/config/reasoning-args.d.ts +9 -7
  6. package/dist/config/reasoning-args.js +12 -10
  7. package/dist/config/reasoning.d.ts +44 -105
  8. package/dist/config/reasoning.js +27 -704
  9. package/dist/config/register.d.ts +34 -48
  10. package/dist/config/register.js +41 -51
  11. package/dist/config/tool-list.d.ts +16 -16
  12. package/dist/config/tool-list.js +1 -1
  13. package/dist/remote/bridge.d.ts +19 -10
  14. package/dist/remote/bridge.js +3 -2
  15. package/dist/remote/broadcast.js +3 -1
  16. package/dist/remote/events.js +12 -11
  17. package/dist/remote/history.d.ts +1 -1
  18. package/dist/remote/protocol.d.ts +6 -3
  19. package/dist/remote/protocol.js +2 -1
  20. package/dist/remote/push.d.ts +16 -16
  21. package/dist/remote/push.js +27 -27
  22. package/dist/remote/register.d.ts +3 -3
  23. package/dist/remote/register.js +17 -19
  24. package/dist/remote/server.d.ts +9 -8
  25. package/dist/remote/server.js +15 -14
  26. package/dist/remote/session-state.d.ts +5 -4
  27. package/dist/remote/session-state.js +8 -5
  28. package/dist/remote/sw.d.ts +7 -6
  29. package/dist/remote/sw.js +7 -6
  30. package/dist/remote/tailscale.d.ts +4 -2
  31. package/dist/remote/tailscale.js +4 -2
  32. package/dist/remote/ui-highlight.js +6 -5
  33. package/dist/remote/ui-render.js +4 -4
  34. package/dist/remote/ui-script.js +24 -24
  35. package/dist/remote/ui-styles.d.ts +1 -1
  36. package/dist/remote/ui-styles.js +10 -13
  37. package/dist/remote/ui-tools.js +9 -6
  38. package/dist/shared/child-extensions.d.ts +29 -17
  39. package/dist/shared/child-extensions.js +29 -17
  40. package/dist/shared/child-output.d.ts +30 -24
  41. package/dist/shared/child-output.js +25 -17
  42. package/dist/shared/child-process.d.ts +47 -40
  43. package/dist/shared/child-process.js +50 -59
  44. package/dist/shared/command-watchdog.d.ts +22 -16
  45. package/dist/shared/command-watchdog.js +28 -21
  46. package/dist/shared/fs-text.d.ts +16 -10
  47. package/dist/shared/fs-text.js +16 -10
  48. package/dist/shared/git-runner.d.ts +25 -25
  49. package/dist/shared/git-runner.js +25 -25
  50. package/dist/shared/leaked-tool-call.d.ts +17 -11
  51. package/dist/shared/leaked-tool-call.js +23 -15
  52. package/dist/shared/model-endpoint.d.ts +29 -16
  53. package/dist/shared/model-endpoint.js +33 -21
  54. package/dist/shared/pi-invocation.d.ts +7 -4
  55. package/dist/shared/pi-invocation.js +12 -7
  56. package/dist/shared/pkg-version.d.ts +13 -5
  57. package/dist/shared/pkg-version.js +13 -5
  58. package/dist/shared/reasoning-capability.d.ts +35 -24
  59. package/dist/shared/reasoning-capability.js +35 -24
  60. package/dist/shared/stream-watchdog.d.ts +60 -44
  61. package/dist/shared/stream-watchdog.js +62 -45
  62. package/dist/task/accept-debt.d.ts +41 -43
  63. package/dist/task/accept-debt.js +73 -65
  64. package/dist/task/api-synthesis.d.ts +24 -21
  65. package/dist/task/api-synthesis.js +32 -26
  66. package/dist/task/apis-contract.d.ts +32 -64
  67. package/dist/task/apis-contract.js +32 -64
  68. package/dist/task/artifact-closure.d.ts +27 -13
  69. package/dist/task/artifact-closure.js +95 -67
  70. package/dist/task/auto-commit.d.ts +46 -35
  71. package/dist/task/auto-commit.js +51 -38
  72. package/dist/task/auto-io.d.ts +45 -25
  73. package/dist/task/auto-io.js +57 -29
  74. package/dist/task/auto-orchestrator.d.ts +26 -24
  75. package/dist/task/auto-orchestrator.js +178 -162
  76. package/dist/task/auto-prompts.d.ts +36 -24
  77. package/dist/task/auto-prompts.js +40 -26
  78. package/dist/task/autofix-ledger.d.ts +27 -25
  79. package/dist/task/autofix-ledger.js +29 -26
  80. package/dist/task/batch-test-task.d.ts +20 -12
  81. package/dist/task/batch-test-task.js +67 -60
  82. package/dist/task/boot-probe.d.ts +60 -44
  83. package/dist/task/boot-probe.js +91 -72
  84. package/dist/task/cancel-input.d.ts +30 -16
  85. package/dist/task/cancel-input.js +20 -11
  86. package/dist/task/cancel-points.d.ts +27 -20
  87. package/dist/task/cancel-points.js +30 -22
  88. package/dist/task/child-runner.d.ts +46 -51
  89. package/dist/task/child-runner.js +48 -49
  90. package/dist/task/child-status.d.ts +23 -16
  91. package/dist/task/child-status.js +23 -16
  92. package/dist/task/clamp-output.js +12 -5
  93. package/dist/task/command-run.d.ts +31 -28
  94. package/dist/task/command-run.js +44 -35
  95. package/dist/task/command-shrink.d.ts +25 -18
  96. package/dist/task/command-shrink.js +37 -31
  97. package/dist/task/command-watchdog.d.ts +9 -6
  98. package/dist/task/command-watchdog.js +21 -15
  99. package/dist/task/context-attribution.d.ts +34 -26
  100. package/dist/task/context-attribution.js +34 -26
  101. package/dist/task/context-silence.d.ts +39 -29
  102. package/dist/task/context-silence.js +35 -25
  103. package/dist/task/context-usage.d.ts +16 -9
  104. package/dist/task/context-usage.js +16 -9
  105. package/dist/task/contracts.d.ts +8 -4
  106. package/dist/task/contracts.js +25 -17
  107. package/dist/task/coverage-loop.d.ts +22 -18
  108. package/dist/task/coverage-loop.js +35 -30
  109. package/dist/task/critique-probes.d.ts +13 -14
  110. package/dist/task/critique-probes.js +50 -39
  111. package/dist/task/debug-log.d.ts +13 -5
  112. package/dist/task/debug-log.js +32 -20
  113. package/dist/task/decompose-fidelity.d.ts +11 -9
  114. package/dist/task/decompose-fidelity.js +38 -33
  115. package/dist/task/decompose-granularity.d.ts +41 -38
  116. package/dist/task/decompose-granularity.js +41 -38
  117. package/dist/task/deep-render-check.d.ts +22 -14
  118. package/dist/task/deep-render-check.js +40 -31
  119. package/dist/task/dropped-input.d.ts +12 -7
  120. package/dist/task/dropped-input.js +5 -2
  121. package/dist/task/enforce-attribution.d.ts +38 -47
  122. package/dist/task/enforce-attribution.js +46 -52
  123. package/dist/task/enforce-guidelines.d.ts +31 -20
  124. package/dist/task/enforce-guidelines.js +32 -21
  125. package/dist/task/enrichment.d.ts +7 -2
  126. package/dist/task/enrichment.js +26 -14
  127. package/dist/task/env-notes.d.ts +16 -7
  128. package/dist/task/env-notes.js +48 -31
  129. package/dist/task/env-template-closure.d.ts +4 -4
  130. package/dist/task/env-template-closure.js +42 -34
  131. package/dist/task/external-context.d.ts +28 -21
  132. package/dist/task/external-context.js +17 -12
  133. package/dist/task/failure-classifier.d.ts +4 -5
  134. package/dist/task/failure-classifier.js +6 -7
  135. package/dist/task/file-inventory.d.ts +15 -11
  136. package/dist/task/file-inventory.js +25 -22
  137. package/dist/task/final-gate-fix.d.ts +74 -86
  138. package/dist/task/final-gate-fix.js +97 -116
  139. package/dist/task/final-gate-progress.d.ts +29 -46
  140. package/dist/task/final-gate-progress.js +40 -51
  141. package/dist/task/final-gate.d.ts +64 -97
  142. package/dist/task/final-gate.js +192 -199
  143. package/dist/task/fix-child.d.ts +21 -27
  144. package/dist/task/fix-child.js +21 -27
  145. package/dist/task/foreign-path.d.ts +6 -5
  146. package/dist/task/foreign-path.js +0 -0
  147. package/dist/task/frozen-conflict.d.ts +9 -10
  148. package/dist/task/frozen-conflict.js +61 -64
  149. package/dist/task/frozen-path-guard.d.ts +35 -14
  150. package/dist/task/frozen-path-guard.js +56 -39
  151. package/dist/task/gate-child.d.ts +27 -28
  152. package/dist/task/gate-child.js +36 -35
  153. package/dist/task/gate-deps.d.ts +34 -27
  154. package/dist/task/gate-deps.js +169 -159
  155. package/dist/task/gate-tally.d.ts +77 -80
  156. package/dist/task/gate-tally.js +65 -68
  157. package/dist/task/git-state-guard.d.ts +15 -11
  158. package/dist/task/git-state-guard.js +76 -66
  159. package/dist/task/impl-widget.d.ts +25 -16
  160. package/dist/task/impl-widget.js +27 -17
  161. package/dist/task/implementation-thinking.d.ts +33 -31
  162. package/dist/task/implementation-thinking.js +5 -6
  163. package/dist/task/implementation-turn.d.ts +34 -31
  164. package/dist/task/implementation-turn.js +29 -27
  165. package/dist/task/inline-markdown.d.ts +20 -7
  166. package/dist/task/inline-markdown.js +15 -6
  167. package/dist/task/launch-config-gap.js +25 -39
  168. package/dist/task/launch-contract.d.ts +18 -21
  169. package/dist/task/launch-contract.js +28 -30
  170. package/dist/task/launch-manifest.d.ts +6 -2
  171. package/dist/task/launch-manifest.js +35 -34
  172. package/dist/task/ledger.js +16 -14
  173. package/dist/task/lint-fix.d.ts +6 -8
  174. package/dist/task/lint-fix.js +67 -69
  175. package/dist/task/loop-detector.d.ts +9 -8
  176. package/dist/task/loop-detector.js +16 -12
  177. package/dist/task/mid-run-input.d.ts +17 -15
  178. package/dist/task/mid-run-input.js +17 -15
  179. package/dist/task/orchestrator.d.ts +24 -28
  180. package/dist/task/orchestrator.js +62 -64
  181. package/dist/task/orientation.d.ts +18 -23
  182. package/dist/task/orientation.js +24 -31
  183. package/dist/task/owned-freeze-conflict.d.ts +21 -20
  184. package/dist/task/owned-freeze-conflict.js +52 -85
  185. package/dist/task/owned-freeze-reassign.d.ts +40 -60
  186. package/dist/task/owned-freeze-reassign.js +41 -61
  187. package/dist/task/parsers.d.ts +4 -2
  188. package/dist/task/parsers.js +4 -4
  189. package/dist/task/phases.d.ts +41 -48
  190. package/dist/task/phases.js +179 -248
  191. package/dist/task/plan-io.d.ts +6 -7
  192. package/dist/task/plan-io.js +6 -7
  193. package/dist/task/plan-orchestrator.d.ts +10 -8
  194. package/dist/task/plan-orchestrator.js +14 -10
  195. package/dist/task/plan-prompts.d.ts +6 -5
  196. package/dist/task/plan-prompts.js +6 -5
  197. package/dist/task/plan-readonly.d.ts +4 -5
  198. package/dist/task/plan-readonly.js +4 -5
  199. package/dist/task/plan-rounds.d.ts +17 -29
  200. package/dist/task/plan-rounds.js +21 -34
  201. package/dist/task/plan-session.d.ts +58 -72
  202. package/dist/task/plan-session.js +61 -83
  203. package/dist/task/probe-gaming.d.ts +28 -27
  204. package/dist/task/probe-gaming.js +0 -0
  205. package/dist/task/prohibition-probe.d.ts +14 -16
  206. package/dist/task/prompts.d.ts +3 -4
  207. package/dist/task/prompts.js +17 -26
  208. package/dist/task/qa-transcript.d.ts +15 -22
  209. package/dist/task/qa-transcript.js +15 -21
  210. package/dist/task/question-box.d.ts +17 -13
  211. package/dist/task/question-box.js +19 -15
  212. package/dist/task/question-dedup.d.ts +6 -7
  213. package/dist/task/question-dedup.js +13 -14
  214. package/dist/task/question-dialog.d.ts +22 -32
  215. package/dist/task/question-dialog.js +22 -32
  216. package/dist/task/question-source.d.ts +18 -44
  217. package/dist/task/question-source.js +22 -51
  218. package/dist/task/refuted-constraint.d.ts +11 -31
  219. package/dist/task/refuted-constraint.js +27 -51
  220. package/dist/task/regenerable-artifacts.d.ts +12 -31
  221. package/dist/task/regenerable-artifacts.js +12 -31
  222. package/dist/task/render-check.d.ts +11 -22
  223. package/dist/task/render-check.js +33 -46
  224. package/dist/task/repo-health-check.d.ts +10 -14
  225. package/dist/task/repo-health-check.js +17 -23
  226. package/dist/task/requirements.d.ts +38 -71
  227. package/dist/task/requirements.js +78 -126
  228. package/dist/task/research-fanout-budget.d.ts +51 -88
  229. package/dist/task/research-fanout-budget.js +51 -88
  230. package/dist/task/research-worker.d.ts +29 -39
  231. package/dist/task/research-worker.js +37 -61
  232. package/dist/task/resume-gap.d.ts +14 -15
  233. package/dist/task/root-cause-repair.d.ts +9 -9
  234. package/dist/task/root-cause-repair.js +28 -40
  235. package/dist/task/run-bracket.d.ts +10 -13
  236. package/dist/task/run-end.d.ts +12 -22
  237. package/dist/task/run-end.js +8 -16
  238. package/dist/task/run-final-gate.d.ts +19 -21
  239. package/dist/task/run-final-gate.js +62 -80
  240. package/dist/task/runner-globs.d.ts +12 -13
  241. package/dist/task/runner-globs.js +12 -13
  242. package/dist/task/runner-resolve.d.ts +9 -9
  243. package/dist/task/runner-resolve.js +22 -23
  244. package/dist/task/script-escape.d.ts +10 -12
  245. package/dist/task/script-escape.js +13 -14
  246. package/dist/task/serve-entry.d.ts +1 -1
  247. package/dist/task/serve-entry.js +22 -25
  248. package/dist/task/service-blocks.js +4 -2
  249. package/dist/task/shipped-source.d.ts +11 -29
  250. package/dist/task/shipped-source.js +11 -29
  251. package/dist/task/skip-escape.js +10 -14
  252. package/dist/task/spec-urls.d.ts +26 -65
  253. package/dist/task/spec-urls.js +26 -65
  254. package/dist/task/spec-validation.d.ts +17 -20
  255. package/dist/task/spec-validation.js +17 -20
  256. package/dist/task/stall-detector.d.ts +23 -30
  257. package/dist/task/stall-detector.js +23 -30
  258. package/dist/task/stream-watchdog.d.ts +14 -12
  259. package/dist/task/stream-watchdog.js +14 -12
  260. package/dist/task/substitution-probe.d.ts +17 -20
  261. package/dist/task/substitution-probe.js +17 -20
  262. package/dist/task/task-gates.d.ts +36 -41
  263. package/dist/task/task-gates.js +95 -106
  264. package/dist/task/task-io.d.ts +4 -4
  265. package/dist/task/task-io.js +4 -4
  266. package/dist/task/task-parsers.js +4 -3
  267. package/dist/task/task-provenance.d.ts +2 -2
  268. package/dist/task/task-provenance.js +11 -13
  269. package/dist/task/task-types.d.ts +4 -3
  270. package/dist/task/terminal-outcome.d.ts +14 -16
  271. package/dist/task/terminal-outcome.js +12 -14
  272. package/dist/task/test-assembly.d.ts +13 -20
  273. package/dist/task/test-assembly.js +13 -20
  274. package/dist/task/timings.d.ts +5 -3
  275. package/dist/task/timings.js +5 -3
  276. package/dist/task/title-label.d.ts +9 -4
  277. package/dist/task/title-label.js +9 -4
  278. package/dist/task/type-only-answer.d.ts +44 -52
  279. package/dist/task/type-only-answer.js +44 -52
  280. package/dist/task/unfailable-command.d.ts +18 -24
  281. package/dist/task/unfailable-command.js +21 -27
  282. package/dist/task/unknown-routing.d.ts +10 -4
  283. package/dist/task/unknown-routing.js +10 -4
  284. package/dist/task/user-directives.d.ts +5 -8
  285. package/dist/task/user-directives.js +5 -8
  286. package/dist/task/verify-quality.d.ts +18 -22
  287. package/dist/task/verify-quality.js +45 -46
  288. package/dist/task/verify-reconcile.d.ts +15 -10
  289. package/dist/task/verify-reconcile.js +45 -43
  290. package/dist/task/verify-resolution.d.ts +24 -20
  291. package/dist/task/verify-resolution.js +51 -50
  292. package/dist/task/verify-work.d.ts +59 -66
  293. package/dist/task/verify-work.js +101 -138
  294. package/dist/task/widget.d.ts +15 -14
  295. package/dist/task/widget.js +22 -17
  296. package/dist/task/wiring-claims.d.ts +25 -32
  297. package/dist/task/wiring-claims.js +30 -35
  298. package/dist/task/write-guard.d.ts +39 -39
  299. package/dist/task/write-guard.js +48 -51
  300. package/dist/task/yolo.d.ts +34 -30
  301. package/dist/task/yolo.js +42 -37
  302. package/dist/workers/abstention.d.ts +21 -41
  303. package/dist/workers/abstention.js +27 -48
  304. package/dist/workers/brave-search.d.ts +4 -3
  305. package/dist/workers/brave-search.js +5 -2
  306. package/dist/workers/brave-warning.d.ts +7 -4
  307. package/dist/workers/brave-warning.js +19 -7
  308. package/dist/workers/ddg-search.d.ts +6 -6
  309. package/dist/workers/ddg-search.js +18 -12
  310. package/dist/workers/docs-cache.js +5 -2
  311. package/dist/workers/docs-chunk.d.ts +30 -37
  312. package/dist/workers/docs-chunk.js +37 -41
  313. package/dist/workers/docs-core.d.ts +28 -44
  314. package/dist/workers/docs-core.js +25 -44
  315. package/dist/workers/docs-index.js +4 -3
  316. package/dist/workers/docs-lookup.d.ts +15 -22
  317. package/dist/workers/docs-lookup.js +12 -21
  318. package/dist/workers/docs-project.d.ts +15 -9
  319. package/dist/workers/docs-project.js +17 -10
  320. package/dist/workers/docs-resolve.d.ts +19 -20
  321. package/dist/workers/docs-resolve.js +35 -32
  322. package/dist/workers/docs-retrieve.d.ts +5 -6
  323. package/dist/workers/docs-retrieve.js +18 -15
  324. package/dist/workers/exa-search.d.ts +9 -6
  325. package/dist/workers/exa-search.js +23 -12
  326. package/dist/workers/fetch-core.d.ts +13 -16
  327. package/dist/workers/fetch-core.js +23 -23
  328. package/dist/workers/focused-extractor.d.ts +12 -12
  329. package/dist/workers/focused-extractor.js +16 -19
  330. package/dist/workers/html-clean.js +24 -14
  331. package/dist/workers/http-request.d.ts +28 -20
  332. package/dist/workers/http-request.js +22 -17
  333. package/dist/workers/npm-version.d.ts +28 -11
  334. package/dist/workers/npm-version.js +24 -15
  335. package/dist/workers/phantom-imports.d.ts +15 -12
  336. package/dist/workers/phantom-imports.js +30 -24
  337. package/dist/workers/pi-worker-core.d.ts +69 -71
  338. package/dist/workers/pi-worker-core.js +100 -109
  339. package/dist/workers/pi-worker-docs.d.ts +24 -19
  340. package/dist/workers/pi-worker-docs.js +67 -76
  341. package/dist/workers/pi-worker-fetch.d.ts +7 -3
  342. package/dist/workers/pi-worker-fetch.js +27 -19
  343. package/dist/workers/pi-worker-search.js +12 -8
  344. package/dist/workers/pi-worker.d.ts +9 -4
  345. package/dist/workers/pi-worker.js +21 -14
  346. package/dist/workers/reasoning-warning.d.ts +18 -17
  347. package/dist/workers/reasoning-warning.js +22 -20
  348. package/dist/workers/research-cache.js +50 -78
  349. package/dist/workers/search-core.js +7 -5
  350. package/dist/workers/search-types.d.ts +10 -9
  351. package/dist/workers/search-types.js +9 -8
  352. package/dist/workers/session-hint.d.ts +13 -14
  353. package/dist/workers/session-hint.js +8 -9
  354. package/dist/workers/shared.d.ts +21 -25
  355. package/dist/workers/shared.js +0 -0
  356. package/dist/workers/single-read-extension.d.ts +14 -7
  357. package/dist/workers/single-read-extension.js +14 -7
  358. package/dist/workers/single-read-guard.d.ts +25 -28
  359. package/dist/workers/single-read-guard.js +32 -32
  360. package/dist/workers/typeonly-log.d.ts +12 -9
  361. package/dist/workers/typeonly-log.js +29 -33
  362. package/dist/workers/worker-channels.d.ts +15 -23
  363. package/dist/workers/worker-channels.js +15 -23
  364. package/dist/workers/worker-failure.d.ts +38 -46
  365. package/dist/workers/worker-failure.js +31 -39
  366. package/dist/workers/worker-kill.d.ts +25 -26
  367. package/dist/workers/worker-kill.js +16 -19
  368. package/dist/workers/worker-profiles.d.ts +43 -53
  369. package/dist/workers/worker-profiles.js +30 -38
  370. package/package.json +10 -8
@@ -1,17 +1,23 @@
1
1
  /**
2
2
  * gate-deps — builds the concrete {@link GateDeps} (verify / enforce / recommend /
3
- * commit / revert) that the post-implementation gate sequence drives.
3
+ * commit / revert) that the post-implementation gate sequence drives. Both
4
+ * /task-auto and the single /task command construct the gate children through it,
5
+ * so they are identical.
4
6
  *
5
- * Lifted out of /task-auto's `defaultDeps` so both /task-auto and the single /task
6
- * command construct the gate children identically. `runTask` (the AUTOFIX re-run) is
7
- * INJECTED rather than imported, so this module never imports the orchestrators —
8
- * keeping the dependency graph acyclic (the orchestrators import this).
7
+ * `runTask` (the AUTOFIX re-run) is INJECTED rather than imported, which is what
8
+ * keeps the graph acyclic: this module names the orchestrators only in comments,
9
+ * while orchestrator.ts, auto-orchestrator.ts and plan-orchestrator.ts all import
10
+ * from here.
9
11
  *
10
- * The gate children verify, the post-FAIL recommend, and enforce are read-only
11
- * (or read,edit for enforce) passes of the same local model that must run to
12
- * completion: unguarded (no wall-clock timeout, exact-match loop guard only,
13
- * path-revisit disabled because re-running the same check IS the job), each with a
14
- * status widget and a per-gate debug log under .pi-tasks/.
12
+ * The gate children run on the `gate` worker profile: no wall-clock timeout, and
13
+ * the path-revisit loop rule disabled because re-running the same check IS the
14
+ * job, leaving only the exact-match rule. Each gets a status widget and a per-gate
15
+ * debug log under `.pi-tasks/`.
16
+ *
17
+ * NONE of them is read-only by capability. `VERIFY_TOOLS` and `RESOLUTION_TOOLS`
18
+ * are both `read,bash` and `ENFORCE_TOOLS` is `read,edit`, so verify and recommend
19
+ * are read-only by CONTRACT only — which is exactly why GATE_CHILD_KINDS marks
20
+ * those two `guarded` and the git-state guard restores what they move.
15
21
  */
16
22
  import { existsSync, readFileSync } from 'node:fs';
17
23
  import * as fsp from 'node:fs/promises';
@@ -47,12 +53,15 @@ import { makeDebugAppender } from './debug-log.js';
47
53
  import { startAutoLoader } from './widget.js';
48
54
  import { ChildStatus } from './child-status.js';
49
55
  import { makeGateChild } from './gate-child.js';
50
- /** Max chars of a tool result kept in the gate debug log (mx5 run 10 item 6). */
56
+ /** Max chars of a tool result kept in the gate debug log. */
51
57
  const TOOL_RESULT_LOG_LIMIT = 300;
52
58
  /**
53
59
  * One-line, tail-kept, whitespace-flattened summary of a tool's output for the gate
54
- * debug log. The TAIL is kept (a bind failure / final status / assertion lands at the
55
- * end of the output) with a leading ellipsis when truncated; empty output → "(no output)".
60
+ * debug log. The TAIL is kept a bind failure, a final status or an assertion
61
+ * lands at the END of the output with a leading ellipsis when truncated.
62
+ * Measured: all-whitespace and empty input both give "(no output)", internal
63
+ * newlines collapse to single spaces, and a 410-char input comes back 301 chars
64
+ * long, starting `…` and ending on the input's last characters.
56
65
  */
57
66
  export function truncateToolResult(text, limit = TOOL_RESULT_LOG_LIMIT) {
58
67
  const flat = text.replace(/\s+/g, ' ').trim();
@@ -67,11 +76,14 @@ const EXCLUDE_TASKS_DIR = ':(exclude).pi-tasks';
67
76
  *
68
77
  * `parseAddedLines` reads the file out of the `+++ b/…` header, but the prefix is
69
78
  * user-configurable: `diff.mnemonicPrefix=true` emits `i/` and `w/` instead of
70
- * `a/` and `b/`, and `diff.noprefix=true` emits none. Both are common developer
71
- * settings, and under either one every added line was attributed to a path like
72
- * `w/src/app.ts`, which exists nowhere — so the probe-gaming and sandbox-path
73
- * probes silently read a diff of files that are not in the repo. Passing the
74
- * prefixes explicitly makes the output independent of the host's git config.
79
+ * `a/` and `b/`, and `diff.noprefix=true` emits none. Not hypothetical: this
80
+ * machine has `diff.mnemonicPrefix=true` set globally, and measured on git 2.55.0,
81
+ * an unpinned `git diff HEAD` here emits `--- c/src/app.ts` / `+++ w/src/app.ts`
82
+ * (a bare `git diff` emits `i/` and `w/`). Every added line would then be
83
+ * attributed to `w/src/app.ts`, a path that exists nowhere, and the probe-gaming
84
+ * and sandbox-path probes would silently read a diff of files not in the repo.
85
+ * With these two arguments the header comes back as `a/` and `b/` under
86
+ * mnemonicPrefix and under noprefix alike.
75
87
  */
76
88
  const DIFF_PREFIX_ARGS = ['--src-prefix=a/', '--dst-prefix=b/'];
77
89
  const splitLines = (s) => s
@@ -116,7 +128,7 @@ export async function collectChangedFiles(cwd, signal) {
116
128
  }
117
129
  /**
118
130
  * Collect the task's added lines WITH CONTENT (path + text) for the probe-gaming
119
- * probe (F6), which needs the actual line text the numstat-shape collector omits.
131
+ * probe, which needs the actual line text the numstat-shape collector omits.
120
132
  * Pre-commit the work is the tree-vs-HEAD diff plus untracked files (read whole, as
121
133
  * all-added); on the post-enforce re-verify the tree is clean, so fall back to the
122
134
  * last commit's diff. Failures degrade to an empty list — the probe is a sharpener,
@@ -143,10 +155,10 @@ export async function collectAddedLines(cwd, signal) {
143
155
  return lines;
144
156
  }
145
157
  /**
146
- * Deterministic sandbox-path-leak pass (see foreign-path.ts, mx5 run 13 PROMPT 4
147
- * item 1): find absolute paths the task committed that resolve nowhere on this
148
- * machine while the real file sits in the repo, REPAIR the ones whose relative
149
- * form provably resolves, and return verify findings for whatever is left.
158
+ * Deterministic sandbox-path-leak pass (see foreign-path.ts): find absolute paths
159
+ * the task committed that resolve nowhere on this machine while the real file sits
160
+ * in the repo, REPAIR the ones whose relative form provably resolves, and return
161
+ * verify findings for whatever is left.
150
162
  *
151
163
  * The repair runs here, before the verify child, for the same reason lint-fix
152
164
  * does: the defect is mechanical and the correct target is already known, so
@@ -176,9 +188,8 @@ export async function collectForeignPathFindings(cwd, signal, logDebug) {
176
188
  /** Manifests whose `scripts` the check-script scanner understands. */
177
189
  const MANIFEST_RE = /(^|\/)package\.json$/;
178
190
  /**
179
- * Deterministic neutered-check-script pass (see script-escape.ts, mx5 run 13 PROMPT
180
- * 4 item 4): check-class scripts that cannot report failure, in a manifest THIS
181
- * task changed.
191
+ * Deterministic neutered-check-script pass (see script-escape.ts): check-class
192
+ * scripts that cannot report failure, in a manifest THIS task changed.
182
193
  *
183
194
  * Scoped to manifests the task touched, so the finding lands on the task that
184
195
  * authored the script rather than being re-served to every later task. A script
@@ -219,10 +230,9 @@ async function readOrNull(cwd, rel) {
219
230
  }
220
231
  }
221
232
  /**
222
- * Deterministic test-runner glob-collision pass (see runner-globs.ts, mx5 runs 7 AND
223
- * 13, PROMPT 4 item 2): the manifest declares both `bun test` and `playwright test`
224
- * without a provably disjoint file set, so `bun test` imports the playwright specs
225
- * and dies during collection.
233
+ * Deterministic test-runner glob-collision pass (see runner-globs.ts): the manifest
234
+ * declares both `bun test` and `playwright test` without a provably disjoint file
235
+ * set, so `bun test` imports the playwright specs and dies during collection.
226
236
  *
227
237
  * Whole-repo rather than diff-scoped, unlike the neutered-script probe: a collision
228
238
  * is a property of the PAIR of declarations, and the task that completes the pair is
@@ -267,10 +277,15 @@ export async function collectTreeChanges(cwd, signal) {
267
277
  return r.exitCode === 0 ? parseTreeChanges(r.stdout) : { modified: [], deleted: [], added: [] };
268
278
  }
269
279
  /**
270
- * Build output directories declared by the project's OWN build commands
271
- * (`--outdir=X`, `--out-dir X`), so the ignored-write exemption follows the real
272
- * tooling instead of a name list. Best-effort: an unreadable or non-JSON manifest
273
- * contributes nothing and the name-list fallback in classifyIgnoredPath applies.
280
+ * Build output directories declared by the project's OWN build commands, so the
281
+ * ignored-write exemption follows the real tooling instead of a name list.
282
+ *
283
+ * The pattern is `--outdir` or `--out-dir`, LOWERCASE, followed by `=` or a space.
284
+ * Measured on a manifest declaring all four spellings: `--outdir=./dist/` yields
285
+ * `dist` and `--out-dir build` yields `build`, while `--outDir out` (tsc's
286
+ * spelling) yields nothing and a bare `--outdir -` is dropped by the leading-dash
287
+ * guard. Best-effort: an unreadable or non-JSON manifest contributes nothing and
288
+ * the name-list fallback in classifyIgnoredPath applies.
274
289
  */
275
290
  export function parseBuildOutdirs(cwd) {
276
291
  let raw;
@@ -300,17 +315,18 @@ export function parseBuildOutdirs(cwd) {
300
315
  return [...out];
301
316
  }
302
317
  /**
303
- * IGNORED-PATH CHANNEL (mx5 run 19 — see write-guard.ts). A fingerprint of every
318
+ * IGNORED-PATH CHANNEL (see write-guard.ts). A fingerprint of every
304
319
  * ACTIONABLE ignored path (`git status --porcelain --ignored=matching`, minus
305
320
  * build output / node_modules / .pi-tasks / .git), taken before and after a
306
321
  * write-capable gate child so its writes to files git never reports are
307
322
  * attributable to it.
308
323
  *
309
324
  * `--ignored=matching` collapses a wholly-ignored directory into ONE entry, which
310
- * is what keeps this cheap: `node_modules/` is one exempt line, never 40,000
311
- * stats. Every failure mode degrades to `{}` no git, an older git that rejects
312
- * `--ignored=matching`, an unreadable pathso the gate behaves exactly as it did
313
- * before this channel existed.
325
+ * is what keeps this cheap: run against a repo ignoring `logs/`, `node_modules/`
326
+ * and `.env`, with files in all three, it reports exactly three lines
327
+ * `!! .env`, `!! logs/`, `!! node_modules/` never one per file. Every failure
328
+ * mode degrades to `{}` (no git, a git that rejects the flag, an unreadable path),
329
+ * so the gate behaves as if the channel were absent.
314
330
  */
315
331
  export async function collectIgnoredSnapshot(cwd, signal) {
316
332
  const r = await git(cwd, ['status', '--porcelain', '--ignored=matching', '--', '.', EXCLUDE_TASKS_DIR], signal);
@@ -356,9 +372,11 @@ export async function gatePassesWithoutIgnored(cwd, paths, runGate, log) {
356
372
  try {
357
373
  for (const rel of paths) {
358
374
  // `--ignored=matching` reports a wholly-ignored DIRECTORY with a trailing
359
- // slash (`logs/`). Left on, `${path.join(cwd, 'logs/')}.pi-gate-probe`
360
- // names a path INSIDE the directory, so the rename is a move-into-itself
361
- // and the probe silently answers null for every directory entry.
375
+ // slash — measured, it prints `logs/`, not `logs`. Left on,
376
+ // `path.join('/repo', 'logs/')` is `/repo/logs/`, so the probe target
377
+ // becomes `/repo/logs/.pi-gate-probe` a path INSIDE the directory, and
378
+ // the rename is a move-into-itself that answers null for every directory
379
+ // entry. Stripped, the target is the sibling `/repo/logs.pi-gate-probe`.
362
380
  const from = path.join(cwd, rel.replace(/\/+$/, ''));
363
381
  const to = `${from}.pi-gate-probe`;
364
382
  try {
@@ -425,9 +443,9 @@ async function readRepoFile(cwd, rel) {
425
443
  }
426
444
  }
427
445
  /**
428
- * Deterministic test-assembly probe input (see test-assembly.ts, run-8 F4): read the
429
- * task's own changed TEST files plus the repo's tracked source files, and return the
430
- * finding lines naming any test that rebuilds a production assembly it never imports.
446
+ * Deterministic test-assembly probe input: read the task's own changed TEST files
447
+ * plus the repo's tracked source files, and return the finding lines naming any
448
+ * test that rebuilds a production assembly it never imports.
431
449
  * Pure import-graph shape; failures degrade to no findings (the probe is a sharpener,
432
450
  * never a blocker). `changed` is the already-collected task diff, reused so the probe
433
451
  * costs one extra tracked-file listing, not a second diff.
@@ -464,7 +482,7 @@ export async function collectTestAssemblyFindings(cwd, changed, signal) {
464
482
  * section, and an unreadable task file is the same case — both are null, and every
465
483
  * caller already treats null as "nothing to hold the work to" (verify no-ops,
466
484
  * frozen paths are empty, recommend falls back to the title). Never throws. This
467
- * used to be the same four-line try/catch at four gate sites.
485
+ * would otherwise be the same four-line try/catch at four gate sites.
468
486
  */
469
487
  export async function readSpecForVerification(cwd, taskId) {
470
488
  try {
@@ -504,41 +522,38 @@ export function buildVerifyProbes(params) {
504
522
  return Promise.resolve([]);
505
523
  return collectChangedFiles(cwd, signal).then(files => findProhibitionViolations(banned, files));
506
524
  },
507
- // Deterministic cross-task deletion probe (mx5 run 12 PROMPT 2):
508
- // tracked files this task's diff DELETES whose introducing commit
509
- // belongs to a DIFFERENT task — a sibling's committed deliverable
510
- // destroyed (typically to green a check). Injected under rule 4d and
511
- // carried on a FAIL so an ACCEPT records durable debts.
525
+ // Deterministic cross-task deletion probe: tracked files this task's
526
+ // diff DELETES whose introducing commit belongs to a DIFFERENT task —
527
+ // a sibling's committed deliverable destroyed, typically to green a
528
+ // check. Injected under rule 4d and carried on a FAIL so an ACCEPT
529
+ // records durable debts.
512
530
  crossTaskDeletion: () => collectTaskTreeChanges(cwd, signal).then(changes => findCrossTaskDeletions(changes, taskId, rel => taskThatIntroduced(cwd, rel))),
513
- // Deterministic probe-gaming probe (F6): added lines whose stated
514
- // purpose is to make a check pass rather than meet the requirement
515
- // ("return 401 so the verification test passes") become rule-4c
516
- // findings so the child verifies the real requirement, not the check.
531
+ // Deterministic probe-gaming probe: added lines whose stated purpose
532
+ // is to make a check pass rather than meet the requirement ("return
533
+ // 401 so the verification test passes") become rule-4c findings, so
534
+ // the child verifies the real requirement and not the check.
517
535
  probeGaming: () => collectAddedLines(cwd, signal).then(findProbeGaming),
518
- // Deterministic sandbox-path-leak probe (mx5 run 13 PROMPT 4 item
519
- // 1): absolute paths committed from the authoring child's own
520
- // environment (`/workspace/src/shared`) that resolve nowhere here.
521
- // Repaired deterministically where the relative form provably
522
- // resolves; the remainder is injected under rule 4e, whose point is
523
- // that such a path breaks the BUILD so the checks that would have
524
- // caught it report nothing rather than failing.
536
+ // Deterministic sandbox-path-leak probe: absolute paths committed
537
+ // from the authoring child's own environment (`/workspace/src/shared`)
538
+ // that resolve nowhere here. Repaired deterministically where the
539
+ // relative form provably resolves; the remainder is injected under
540
+ // rule 4e, whose point is that such a path breaks the BUILD — so the
541
+ // checks that would have caught it report nothing rather than failing.
525
542
  foreignPath: () => collectForeignPathFindings(cwd, signal, log),
526
- // Deterministic neutered-check-script probe (mx5 run 13 PROMPT 4
527
- // item 4): a check script this task authored that cannot fail
528
- // (`… || true`, an inverted-grep launder). Injected under rule 4f,
529
- // because the child provably cannot find this by running the
530
- // script — it passes, which IS the defect.
543
+ // Deterministic neutered-check-script probe: a check script this task
544
+ // authored that cannot fail (`… || true`, an inverted-grep launder).
545
+ // Injected under rule 4f, because the child cannot find this by
546
+ // running the script it passes, which IS the defect.
531
547
  scriptEscape: () => collectScriptEscapeFindings(cwd, signal),
532
- // Deterministic runner glob-collision probe (mx5 runs 7 AND 13,
533
- // PROMPT 4 item 2): both `bun test` and `playwright test` declared
534
- // with no proof their file sets are disjoint. Injected under rule
535
- // 4g the collision kills the suite during COLLECTION, which does
536
- // not look like a test failure.
548
+ // Deterministic runner glob-collision probe: both `bun test` and
549
+ // `playwright test` declared with no proof their file sets are
550
+ // disjoint. Injected under rule 4g the collision kills the suite
551
+ // during COLLECTION, which does not look like a test failure.
537
552
  runnerGlob: () => collectRunnerGlobFindings(cwd),
538
- // Deterministic test-assembly probe (F4): authored test files that
539
- // rebuild production wiring — importing the leaf modules the shipped
540
- // entry composes and assembling their own copy — become rule-3f
541
- // findings so the child drives the REAL assembly, not the copy.
553
+ // Deterministic test-assembly probe: authored test files that rebuild
554
+ // production wiring — importing the leaf modules the shipped entry
555
+ // composes and assembling their own copy — become rule-3f findings,
556
+ // so the child drives the REAL assembly and not the copy.
542
557
  testAssembly: () => collectChangedFiles(cwd, signal).then(changed => collectTestAssemblyFindings(cwd, changed, signal))
543
558
  };
544
559
  }
@@ -551,10 +566,9 @@ export function buildVerifyProbes(params) {
551
566
  */
552
567
  export function buildGateDeps(params) {
553
568
  const { signal, parentContextWindow, runTask } = params;
554
- // A/B seam (scripts/verify-deadair-ab.ts), same shape as CANCEL_AB_ARM in
555
- // cancel-points.ts: reproduce the pre-fix gate blocking sync repo health, no
556
- // loader across the deterministic stage — so the dead air can be measured in the
557
- // SAME binary rather than against a remembered baseline. Unset in every real run.
569
+ // A/B seam, same shape as CANCEL_AB_ARM in cancel-points.ts: drop the loader
570
+ // across the deterministic stage so the dead air can be measured in the SAME
571
+ // binary. Unset in every real run.
558
572
  const deadAirBaseline = process.env.DEADAIR_AB_ARM === 'baseline';
559
573
  // ONE live status for every gate child, so the widget mirrors the running
560
574
  // child's latest output line and context usage exactly like the single-task
@@ -565,9 +579,10 @@ export function buildGateDeps(params) {
565
579
  // child run (see git-state-guard.ts). runWorkVerification reads this through its
566
580
  // mutationCheck dep to discard a verdict computed on a mutated tree.
567
581
  let lastGuardReconcile = null;
568
- // Restore tracked files to HEAD and drop files a pass created; the .pi-tasks
569
- // trail/log writes made during the pass survive both. Shared by the enforce
570
- // pre-commit gate (discardEdits) and the final-gate autofix shrink guard.
582
+ // Restore tracked files to HEAD and drop files a pass created. The `.pi-tasks`
583
+ // trail and log writes made during the pass survive both: the checkout excludes
584
+ // that directory by pathspec and the clean excludes it with `-e`. Shared by the
585
+ // enforce pre-commit gate (discardEdits) and the final-gate autofix guards.
571
586
  const discardTreeEdits = async (cwd2) => {
572
587
  await git(cwd2, ['checkout', '--', '.', EXCLUDE_TASKS_DIR], signal);
573
588
  await git(cwd2, ['clean', '-fd', '-e', '.pi-tasks'], signal);
@@ -604,7 +619,7 @@ export function buildGateDeps(params) {
604
619
  // Durable per-task gate trail: every verdict/decision lands in the task
605
620
  // file's `## gates` section so gate behavior is auditable from artifacts.
606
621
  record: (cwd2, taskId, line) => appendGateRecord(cwd2, taskId, line),
607
- // Durable defect ledger under .pi-tasks/ (survives discardEdits): every
622
+ // Durable defect ledger under `.pi-tasks/` (survives discardEdits): every
608
623
  // recorded class — accepted, yolo-accepted, enforce-revert, enforce-kept,
609
624
  // frozen-blocked, cross-task-deletion, root-cause — lands here with its
610
625
  // origin, and the final integration gate re-checks each one at run end.
@@ -643,10 +658,10 @@ export function buildGateDeps(params) {
643
658
  const c = parseTreeChanges(r.stdout);
644
659
  return [...c.modified, ...c.added, ...c.deleted];
645
660
  }
646
- // `enforce-commit` is HEAD ALONE — at the enforce differential HEAD is
647
- // the ENFORCE commit, and that commit's own diff is the only file set
648
- // that can answer "could reverting this repair the failure?" (mx5 run
649
- // 18 / nexttask 4).
661
+ // `enforce-commit` is HEAD ALONE (`log -n 1`) — at the enforce
662
+ // differential HEAD is the ENFORCE commit, and that commit's own diff
663
+ // is the only file set that can answer "could reverting this repair
664
+ // the failure?". Every other scope reads the last two commits.
650
665
  const r = await git(cwd2, [
651
666
  'log',
652
667
  '-n',
@@ -690,19 +705,18 @@ export function buildGateDeps(params) {
690
705
  cwd: cwd2,
691
706
  signal,
692
707
  mode,
693
- // Run the worker child UNGUARDED: no loop detector, no wall-clock
694
- // timeout. This pass reworks files in place until every violation is
695
- // fixed and legitimately reads/edits the same file many times the
696
- // research-worker guards mislabel that as a runaway and kill good work
697
- // (proven on mx5 TASK_0002). classifyEnforceChildFailure still blocks
698
- // on a real failure (non-zero exit, leaked tool call) or a user cancel.
708
+ // Run the worker child with no loop detector and no wall clock. This
709
+ // pass reworks files in place until every violation is fixed and
710
+ // legitimately reads and edits the same file many times, which the
711
+ // research-worker guards would mislabel as a runaway.
712
+ // classifyEnforceChildFailure still blocks on a real failure a
713
+ // non-zero exit, a leaked tool call or a user cancel.
699
714
  //
700
- // This used to be an inline ~85-line copy of the gate-child ritual,
701
- // differing only in the four things GATE_CHILD_KINDS now carries as
702
- // row data: no git-state guard (editing is this pass's job), no
703
- // tool-result logging, no tree-change capture, and its own end
704
- // marker. Its own debug log stays — the enforce child is otherwise
705
- // unobservable.
715
+ // The enforce child goes through the same makeGateChild call as
716
+ // every other kind; what differs is its GATE_CHILD_KINDS row no
717
+ // git-state guard, because editing is this pass's job, no
718
+ // tool-result logging, and its own end marker. Its debug log is
719
+ // separate because the enforce child is otherwise unobservable.
706
720
  runChild: gateChild(enforceCtx, cwd2, taskTitle, 'enforce', 'enforce-debug.log')
707
721
  });
708
722
  },
@@ -715,15 +729,14 @@ export function buildGateDeps(params) {
715
729
  // runWorkVerification treats a null spec as a no-op pass.
716
730
  const spec = await readSpecForVerification(cwd2, taskId);
717
731
  // DEAD AIR (the reason this loader exists). The gate's DETERMINISTIC
718
- // stage — repo health plus ten probes — runs before the verify child,
719
- // and the child's own loader only starts once the child does. The impl
720
- // widget was cleared at `agent_end`, so until now the screen simply
721
- // stopped: no spinner, no clock, no line (the `verifying…` notify cannot
722
- // even paint, since pi-tui schedules renders on process.nextTick and the
723
- // health check used to block the loop outright). MEASURED on real repos:
724
- // 15s (mx5) to 69s (aiz-client) per health run, 0 of 686 expected 100ms
725
- // timer ticks delivered. One loader now spans the WHOLE gate the
726
- // deterministic stage and the child — so the run is never silent.
732
+ // stage — repo health plus the nine PROBE_ADAPTERS rows — runs before
733
+ // the verify child, and the child's own loader only starts once the
734
+ // child does. The implementation widget is cleared at `agent_end`, so
735
+ // without this the screen has no spinner, no clock and no line for the
736
+ // whole stage. A `verifying…` notify would not help: pi-tui schedules
737
+ // every render through `process.nextTick` (pi-tui/dist/tui.js), so a
738
+ // synchronous health check starves the render entirely. One loader spans
739
+ // the WHOLE gate the deterministic stage and the child.
727
740
  const gateStartedAt = Date.now();
728
741
  let stageLine;
729
742
  // `track` clears the PREVIOUS child's trailer before the loader goes
@@ -757,15 +770,13 @@ export function buildGateDeps(params) {
757
770
  },
758
771
  // Deterministic whole-repo static-analysis gate — runs the project's
759
772
  // own lint/typecheck and fails on a real non-zero exit, independent of
760
- // the model-authored VERIFY block (which may not lint at all). ASYNC:
761
- // the sync runner froze the event loop for the whole lint (see above).
762
- // ONE call, both arms. The baseline arm used to call this
763
- // without the signal or the progress hook, because
764
- // `runRepoHealthCheck` was SYNCHRONOUS and blocking the event
765
- // loop was the thing being measured. It is async now, so that
766
- // branch measured treatment against treatment — and it also
767
- // made `DEADAIR_AB_ARM=baseline` silently uncancellable and
768
- // mute. The arm's real difference is the LOADER, above.
773
+ // the model-authored VERIFY block, which may not lint at all. ASYNC,
774
+ // so the lint does not starve pi-tui's nextTick-scheduled renders.
775
+ //
776
+ // ONE call, both arms. A baseline arm that dropped the signal or the
777
+ // progress hook here would be uncancellable and mute for reasons
778
+ // unrelated to the thing under test. The arm's only difference is
779
+ // the LOADER, above.
769
780
  repoHealth: () => runRepoHealthCheck(cwd2, {
770
781
  signal,
771
782
  onCommand: c => {
@@ -786,23 +797,23 @@ export function buildGateDeps(params) {
786
797
  // when the mutation touched graded state (verdictTainted). A child
787
798
  // that merely left test-runner output behind (test-results/,
788
799
  // playwright-report/ …) judged an equivalent tree; its verdict stands
789
- // and the artifacts were still cleaned (mx5 run 9 lost 7 verdicts this
790
- // way see git-state-guard.ts).
800
+ // and the artifacts are still cleaned. See git-state-guard.ts for
801
+ // which mutations count as tainting.
791
802
  mutationCheck: () => lastGuardReconcile?.verdictTainted ?
792
803
  { mutated: true, detail: lastGuardReconcile.actions.join('; ') }
793
804
  : { mutated: false, detail: '' },
794
- // Per-run environment-facts cache under .pi-tasks/ (survives
805
+ // Per-run environment-facts cache under `.pi-tasks/` (survives
795
806
  // discardEdits): earlier children's discoveries save this child
796
807
  // the re-archaeology; its own ENV-NOTE lines are stored for the
797
808
  // next one, stamped with this task's id as their origin so a
798
809
  // later child sees a cited fact is second-hand and must
799
- // re-validate before excusing a failure (F7). Facts only —
800
- // verdict rules unaffected.
810
+ // re-validate before excusing a failure. Facts only — verdict
811
+ // rules unaffected.
801
812
  envNotes: {
802
813
  read: () => readEnvNotes(cwd2),
803
814
  append: notes => appendEnvNotes(cwd2, notes, taskId)
804
815
  },
805
- // Per-run cross-slice contract registry under .pi-tasks/ (F3): the
816
+ // Per-run cross-slice contract registry under `.pi-tasks/`: the
806
817
  // verbatim interface facts the design pins that multiple slices
807
818
  // share, so the verify child checks this slice's boundary against
808
819
  // them. Empty on single-`/task` runs or a design with no shared
@@ -811,12 +822,12 @@ export function buildGateDeps(params) {
811
822
  }));
812
823
  },
813
824
  lintFix: async (fixCtx, cwd2, taskTitle, taskId, failReason) => {
814
- // Same frozen extraction the enforce guard and the verify rule-4b
815
- // probe consume (mx5 run 12: the lint-fix child edited spec-frozen
816
- // tsconfig.json because ESLint's own error text instructed it, then
817
- // verify failed the TASK for that edit the child must know the
818
- // spec's do-not-touch list AND be mechanically denied it).
819
- // (spec unreadable → no frozen paths; the guard degrades to a no-op)
825
+ // Same frozen extraction the enforce guard and the verify rule-4b probe
826
+ // consume. A linter's own error text can instruct an edit inside a
827
+ // spec-frozen file, so the lint-fix child must both KNOW the do-not-touch
828
+ // list and be mechanically denied itknowing alone is not enough when
829
+ // the tool output is telling it otherwise. Spec unreadable → no frozen
830
+ // paths, and the guard degrades to a no-op.
820
831
  const frozenPaths = frozenPathsFromSpec(await readSpecForVerification(cwd2, taskId));
821
832
  return runBoundedLintFix({
822
833
  cwd: cwd2,
@@ -829,20 +840,21 @@ export function buildGateDeps(params) {
829
840
  return { exitCode: r.exitCode, stdout: r.stdout };
830
841
  },
831
842
  frozenPaths,
832
- // Cross-task deletion guard (mx5 run 12 PROMPT 2): the revert-guard
833
- // is blind to a clean tracked sibling deliverable, so deleting one
834
- // greens the lint and the pass returns ok. Provenance is the
835
- // discriminator the guard restores a sibling's deleted file and
836
- // reports not-applied naming the owner.
843
+ // Cross-task deletion guard: the revert-guard only watches files the
844
+ // task had already dirtied, so it is blind to a CLEAN tracked sibling
845
+ // deliverable — deleting one greens the lint and the pass returns ok.
846
+ // Provenance is the discriminator: the guard restores the sibling's
847
+ // deleted file and reports not-applied, naming the owner.
837
848
  currentTaskId: taskId,
838
849
  introducedBy: rel => Promise.resolve(taskThatIntroduced(cwd2, rel))
839
850
  });
840
851
  },
841
852
  // Deterministic static check + tree helpers for the enforce pre-commit gate.
842
- // Runs TWICE per task there (a baseline before the edit pass, a differential
843
- // check after it), each one as long as the project's own lint — so it gets
844
- // the same treatment as the verify-side run: async, and under a live loader
845
- // naming the command, instead of a frozen screen.
853
+ // task-gates.ts calls it up to twice per task in `edit` mode a baseline
854
+ // before the edit pass, and a differential check after it when edits were
855
+ // made. Each run is as long as the project's own lint, so it gets the same
856
+ // treatment as the verify-side run: async, under a live loader naming the
857
+ // command.
846
858
  repoHealth: (healthCtx, cwd2, label) => {
847
859
  const startedAt = Date.now();
848
860
  let running;
@@ -880,28 +892,26 @@ export function buildGateDeps(params) {
880
892
  discoverLabels: discoverGateCommandLabels,
881
893
  discoverBodies: discoverGateCommandBodies,
882
894
  discard: discardTreeEdits,
883
- // WRITE-GUARD STACK (mx5 run 11: this child ran with free bash and
884
- // none of the run-8 guards it rm'd a sibling task's verified
885
- // deliverable and hand-copied a contract to green the lint). Diff
886
- // capture happens at the makeGateChild seam; deletion guard +
887
- // probe scan reject-and-discard here. The frozen-path deny
888
- // (FinalFixDeps.frozenPaths/revertFrozen) is deliberately NOT
889
- // wired: per-task fences are task-SCOPED ("this task must not
890
- // touch a sibling's territory"), and the measured union over the
891
- // run-11 specs would have reverted the one legitimate fix that
892
- // run needed (migrate.ts, frozen by its own producing task). Wire
893
- // it only when a run-GLOBAL freeze source exists (a design-level
894
- // preserve registry), never a per-task union.
895
+ // WRITE-GUARD STACK. This child has free bash, so it needs one.
896
+ // Diff capture happens at the makeGateChild seam, keyed on the
897
+ // child's TOOLS; the deletion guard and the probe scan
898
+ // reject-and-discard inside runFinalGateAutofix. The frozen-path
899
+ // deny (FinalFixDeps.frozenPaths/revertFrozen) is deliberately NOT
900
+ // wired here: per-task fences are task-SCOPED — "this task must not
901
+ // touch a sibling's territory" so their union across a run can
902
+ // fence off a file a legitimate whole-repo fix has to touch. Wire it
903
+ // only when a run-GLOBAL freeze source exists, never a per-task
904
+ // union.
895
905
  treeChanges: () => collectTreeChanges(cwd2, signal),
896
906
  probeScan: () => collectAddedLines(cwd2, signal).then(findProbeGaming),
897
- // IGNORED-PATH CHANNEL (mx5 run 19): the write guards above read
898
- // `git status --porcelain`, which never reports ignored paths, so
899
- // the pass that greened `bun run seed` by writing credentials into
900
- // a gitignored `.env` was structurally invisible to all of them —
901
- // and the gate certified a PASS no fresh clone can reproduce. This
902
- // does not reject the write (a local `.env` is often the only way to
903
- // make a check run); it records it, and downgrades a PASS proven to
904
- // depend on it.
907
+ // IGNORED-PATH CHANNEL: every write guard above reads
908
+ // `git status --porcelain`, which does not report ignored paths, so
909
+ // a pass that greens a command by writing credentials into a
910
+ // gitignored `.env` is structurally invisible to all of them — and
911
+ // the gate would certify a PASS no fresh clone can reproduce. This
912
+ // does not reject the write; a local `.env` is often the only way to
913
+ // make a check run at all. It records it, and downgrades a PASS
914
+ // proven to depend on it.
905
915
  ignoredSnapshot: () => collectIgnoredSnapshot(cwd2, signal),
906
916
  ...(ignoredKnown && ignoredKnown.length > 0 ? { ignoredKnown } : {}),
907
917
  gateWithoutIgnored: paths => gatePassesWithoutIgnored(cwd2, paths, c => runFinalIntegrationGate(c, { signal }), makeDebugAppender(path.join(tasksDir(cwd2), 'final-gate-debug.log'))),