@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
@@ -3,13 +3,10 @@
3
3
  * declares the finished project must expose, extracted once at plan time and diffed
4
4
  * by the final gate against the shipped manifest.
5
5
  *
6
- * The failure this closes (mx5 run 10 item 4): the design's §9 "Build & run" listed
7
- * the required scripts verbatim `dev`, `build`, `migrate`, `seed`, `test` but the
8
- * shipped package.json declared only `dev`, `build`, `lint`, `test`, `test:ct`. No
9
- * task owned `migrate`/`seed` (they fell through decompose), and NOTHING re-checked
10
- * the finished manifest against the design's own list, so the run completed missing
11
- * two of its declared entrypoints. A per-slice gate cannot catch this — it is a
12
- * whole-project launch-surface fact — and the final gate never had the design's list.
6
+ * The gap it closes is structural. A script the design names but no task owned can
7
+ * fall through decompose and ship missing, and a per-slice gate cannot see that: it
8
+ * is a whole-project launch-surface fact, and nothing else re-reads the finished
9
+ * manifest against the design's own list.
13
10
  *
14
11
  * Mechanism (mirrors contracts.ts): a plan-time child EMITs `SCRIPT:` lines naming the
15
12
  * scripts the design declares; the host GROUNDS each against the design — a name is
@@ -17,9 +14,13 @@
17
14
  * form designs use to declare a script. A paraphrase or a script the model invented is
18
15
  * not grounded and is dropped, so the diff can never false-flag on a hallucinated
19
16
  * requirement. The grounded list is appended HOST-SIDE to `.pi-tasks/launch-contract.md`
20
- * (children never write it), which survives discardEdits and the git-state guard.
17
+ * the extraction child runs `--no-tools` and so has no write tool at all. Both halves
18
+ * of `discardTreeEdits` skip that directory (`checkout -- . :(exclude).pi-tasks`, then
19
+ * `clean -fd -e .pi-tasks`), and the git-state guard excludes it from the tree it
20
+ * hashes, so the file outlives a discarded pass.
21
21
  *
22
- * At run end the final gate reads the list, reads the manifest's `scripts`, and FAILs
22
+ * At run end the final gate reads the list, reads the shipped manifest's script names
23
+ * (`readLaunchManifest`: package.json `scripts`, else a Makefile's targets), and FAILs
23
24
  * naming any declared script the manifest is missing. FP-safe by construction: an
24
25
  * empty/ungrounded list (a design that never backticks a script name) yields no check.
25
26
  */
@@ -93,22 +94,20 @@ export function keepGroundedScripts(names, sourceDoc) {
93
94
  return kept;
94
95
  }
95
96
  /**
96
- * DETERMINISTIC RECALL (mx5 run 11): enumerate every backticked, script-name-shaped
97
+ * DETERMINISTIC RECALL: enumerate every backticked, script-name-shaped
97
98
  * token in a paragraph that mentions the word "script", as extraction CANDIDATES.
98
99
  *
99
- * The run-11 failure this closes: `test:ct` is backticked in the design's §2 tooling
100
- * paragraph, so the grounding guard would have KEPT it but the extraction child
101
- * anchored on §9's one-line summary (`dev`,`build`,`migrate`,`seed`,`test`) and never
102
- * emitted it. Grounding can only DROP a candidate, never add one, so recall was
103
- * entirely the model's, over a 20KB doc. This makes recall mechanical: the host
104
- * enumerates candidates and hands them to the child as an explicit checklist; the
105
- * model's job flips from recall (weak) to per-candidate classification (strong).
100
+ * Grounding can only DROP a candidate, never add one, so without this the recall of a
101
+ * script declared far from the design's summary list is entirely the model's. This
102
+ * makes recall mechanical: the host enumerates the candidates and hands them to the
103
+ * child as an explicit checklist, so the model's job flips from recall (weak) to
104
+ * per-candidate classification (strong).
106
105
  *
107
106
  * The paragraph gate (`\bscripts?\b`, word-bounded so "TypeScript"/"JavaScript"
108
107
  * don't match) is a grounded-context filter, not a tuned knob: a design declares a
109
- * script by calling it one. It keeps package-name paragraphs (`hono`, `react`) out
110
- * of the checklist so a weak model isn't invited to keep junk the grounding guard
111
- * would then bless (every package name is backticked somewhere). A design with no
108
+ * script by calling it one. It keeps package-name paragraphs out of the checklist so
109
+ * a weak model isn't invited to keep junk the grounding guard would then bless —
110
+ * every package name is backticked somewhere. A design with no
112
111
  * such paragraph yields no candidates and the prompt is unchanged.
113
112
  */
114
113
  export function enumerateScriptCandidates(sourceDoc) {
@@ -153,13 +152,12 @@ export async function appendDeclaredScripts(cwd, names) {
153
152
  */
154
153
  const BOOT_CLASS_RE = /^(?:dev|start|serve|preview|watch)(?:[:_-].*)?$/i;
155
154
  /**
156
- * The declared scripts the final gate must EXECUTE as one-shot commands (mx5 run
157
- * 11): everything the launch contract declares that is neither boot-class (the
158
- * boot check exercises those) nor already covered by the gate's integration
159
- * commands (`covered`, case-insensitive — the test/build-shaped scripts that ran).
160
- * Run 11 shipped `migrate` and `seed` broken (`.rows` on a Bun sql array —
161
- * TypeError on first call) while the gate checked only that the scripts EXIST;
162
- * existence is not launchability.
155
+ * The declared scripts the final gate must EXECUTE as one-shot commands: everything
156
+ * the launch contract declares that is neither boot-class (the boot check exercises
157
+ * those) nor already covered by the gate's integration commands (`covered`,
158
+ * case-insensitive — the test/build-shaped scripts that ran). Checking only that a
159
+ * script is DECLARED says nothing about whether running it works; existence is not
160
+ * launchability.
163
161
  */
164
162
  export function runnableDeclaredScripts(declared, covered) {
165
163
  const have = new Set(covered.map(s => s.toLowerCase()));
@@ -189,9 +187,9 @@ export function missingDeclaredScripts(declared, manifestScripts) {
189
187
  * (keepGroundedScripts), so a hallucinated script cannot reach the diff.
190
188
  *
191
189
  * `candidates` is enumerateScriptCandidates' mechanical checklist. It exists so the
192
- * model cannot MISS a declared script buried far from the design's summary list (the
193
- * run-11 `test:ct` hole); the model still classifies each candidate against the
194
- * design, and the host grounding still applies. Empty ⇒ the prompt is unchanged.
190
+ * model cannot MISS a declared script buried far from the design's summary list; the
191
+ * model still classifies each candidate against the design, and the host grounding
192
+ * still applies. Empty ⇒ the prompt is unchanged.
195
193
  */
196
194
  export const LAUNCH_EXTRACT_PROMPT = (feature, candidates = []) => [
197
195
  'You are recording the PACKAGE/BUILD SCRIPTS the design below says the finished',
@@ -31,12 +31,16 @@ export declare function makeTargets(src: string): string[];
31
31
  export declare function readLaunchManifest(cwd: string): LaunchManifest;
32
32
  /**
33
33
  * The one line an INERT contract leaves behind. It rides in the gate's UNOBSERVED
34
- * channel rather than in `warnings` for the reason nexttask 16A gives: a check that
34
+ * channel rather than in `warnings` for one reason: a check that
35
35
  * silently did nothing must not read later as a check that passed.
36
36
  */
37
37
  export declare function inertLaunchContractNote(declared: string[], manifest: LaunchManifest): string;
38
38
  /** The `scripts` map of this tree's package.json, or `{}` when there isn't one.
39
39
  * Shared by the gate and the boot probe — both ask the same manifest question. */
40
40
  export declare function packageScripts(cwd: string): Record<string, string>;
41
- /** Does this tree's Makefile declare `target`? */
41
+ /**
42
+ * Does this tree's Makefile declare `target`? Reads the literal name `Makefile`
43
+ * only — unlike {@link readLaunchManifest}, this one does not walk make's lookup
44
+ * order, so a `GNUmakefile`-only tree answers false.
45
+ */
42
46
  export declare function makeHasTarget(cwd: string, target: string): boolean;
@@ -1,54 +1,50 @@
1
1
  /**
2
2
  * launch-manifest — WHICH manifest the launch-contract diff is entitled to diff
3
- * against, and whether there is one at all (nexttask 16A).
3
+ * against, and whether there is one at all.
4
4
  *
5
- * The defect this closes (unobserved, proven by construction). The extraction end
6
- * of the launch contract has no ecosystem test anywhere in it:
7
- * `enumerateScriptCandidates` scrapes backticked, script-name-shaped tokens out of
8
- * any design paragraph that says "script", and `keepGroundedScripts` can only DROP
9
- * a candidate. So a CMake / cargo / poetry / Makefile design saying *"the Makefile
10
- * must expose `build`, `test`, `migrate`"* records exactly the same artifact an npm
11
- * design does. The diff end hardcoded the ecosystem anyway:
5
+ * WHY THE DIFF NEEDS ITS OWN RESOLVER. The extraction end of the launch contract
6
+ * has no ecosystem test anywhere in it: `enumerateScriptCandidates` scrapes
7
+ * backticked, script-name-shaped tokens out of any design paragraph that says
8
+ * "script", and `keepGroundedScripts` can only DROP a candidate. A Makefile design
9
+ * saying *"the Makefile must expose `build`, `test`, `migrate`"* therefore records
10
+ * exactly the same artifact an npm design does.
12
11
  *
13
- * missingDeclaredScripts(declared, Object.keys(packageScripts(cwd)))
14
- * …where packageScripts' catch returns {} so "this project has no
15
- * package.json" was indistinguishable from "its package.json declares no
16
- * scripts", and every declared script was reported missing, at rank 1, in
17
- * wording that NAMES a file the project was never meant to have.
18
- *
19
- * That text seeds the autofix child's prompt (final-gate.ts's FinalGateOutcome.reason),
20
- * so the most likely repair on a CMake project was to write a package.json.
21
- *
22
- * IAR1 is how close this got: `plan-debug.log:10` records "launch-contract
23
- * extraction: 0 grounded script(s) kept from 3 emitted" — a non-npm project that
24
- * reached extraction and emitted three candidates, saved only by its design not
25
- * backticking them in a "script" paragraph.
12
+ * The diff cannot then reach for `packageScripts` alone. Its catch returns `{}`, so
13
+ * "this project has no package.json" is indistinguishable from "its package.json
14
+ * declares no scripts", and every declared script reads as missing — in wording that
15
+ * NAMES a file the project was never meant to have. `tally.fail` text becomes the
16
+ * gate's `reason`, which `buildFinalFixPrompt` hands to the autofix child, so on a
17
+ * non-npm tree the repair that text invites is to write a package.json.
26
18
  *
27
19
  * THE RULE, and it is deliberately two ecosystems wide, not three:
28
20
  * • package.json present and parseable ⇒ diff against its `scripts` keys, exactly
29
- * as before (byte-identical failure text on every npm tree — mx5's missing
21
+ * as before (byte-identical failure text on every npm tree — a missing
30
22
  * `build`/`seed` is a TRUE positive and must keep failing).
31
- * • no package.json but a Makefile ⇒ diff against its TARGETS. The capability was
32
- * already in final-gate.ts (`makeHasTarget`); the diff simply never called it.
23
+ * • no package.json but a Makefile ⇒ diff against its TARGETS.
33
24
  * • neither ⇒ the check is INERT. No failure, and a note saying the contract was
34
25
  * recorded but is not checkable here, so the silence is not read later as a pass
35
26
  * (the repo-health-check / env-template-closure discipline: ENOENT = pass, and a
36
27
  * check must never invent a file the project chose not to have).
37
28
  *
38
- * cargo/poetry/gradle are NOT here on purpose. The corpus on this box contains one
39
- * project that ever recorded a launch contract (mx5, npm) and one non-npm project
40
- * that reached extraction (IAR1, CMake). A third ecosystem would be a guess.
29
+ * cargo/poetry/gradle are NOT here on purpose: nothing in this repo knows how to
30
+ * enumerate their entrypoints, and a third resolver written without one would be a
31
+ * guess that can only produce false FAILs.
41
32
  */
42
33
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
43
34
  import * as path from 'node:path';
44
- /** GNU make's own lookup order, and nothing beyond it — this is one ecosystem. */
35
+ /**
36
+ * GNU make's own lookup order, and nothing beyond it — this is one ecosystem.
37
+ * Confirmed against `make` itself: with all three files present in one directory it
38
+ * runs GNUmakefile; with only `makefile` and `Makefile`, it runs `makefile`.
39
+ */
45
40
  const MAKEFILE_NAMES = ['GNUmakefile', 'makefile', 'Makefile'];
46
41
  /** A target we are willing to name in a failure: an ordinary word-shaped target. */
47
42
  const TARGET_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,39}$/i;
48
43
  /**
49
44
  * A variable assignment, in every form make accepts (`=`, `:=`, `::=`, `?=`, `+=`,
50
45
  * `!=`, with an optional `override`/`export`). Matched BEFORE the target rule
51
- * because `FLAGS ::= -O2` otherwise reads as a target named `FLAGS`.
46
+ * because `FLAGS ::= -O2` otherwise reads as a target named `FLAGS` — make itself
47
+ * reports that line as a variable, not a rule.
52
48
  */
53
49
  const ASSIGN_RE = /^(?:override\s+|export\s+)?[A-Za-z_][A-Za-z0-9_.]*\s*(?:::|:|\?|\+|!)?=/;
54
50
  /**
@@ -87,9 +83,10 @@ export function makeTargets(src) {
87
83
  }
88
84
  /**
89
85
  * The makefiles that really exist in `cwd`, in GNU make's lookup order, named as
90
- * the DIRECTORY spells them. `existsSync` cannot do this job: on a case-insensitive
91
- * filesystem (Windows, macOS) `existsSync('makefile')` is true for a `Makefile`, and
92
- * the failure text would then name a file the project does not have.
86
+ * the DIRECTORY spells them. The invariant is that `manifest.file` is always a name
87
+ * `readdirSync(cwd)` contains, so a failure can never name a file the project does
88
+ * not have. An `existsSync` probe per candidate name cannot promise that, because it
89
+ * answers about a name we chose rather than a name on disk.
93
90
  */
94
91
  function makefilesOnDisk(cwd) {
95
92
  let entries;
@@ -141,7 +138,7 @@ export function readLaunchManifest(cwd) {
141
138
  }
142
139
  /**
143
140
  * The one line an INERT contract leaves behind. It rides in the gate's UNOBSERVED
144
- * channel rather than in `warnings` for the reason nexttask 16A gives: a check that
141
+ * channel rather than in `warnings` for one reason: a check that
145
142
  * silently did nothing must not read later as a check that passed.
146
143
  */
147
144
  export function inertLaunchContractNote(declared, manifest) {
@@ -160,7 +157,11 @@ export function packageScripts(cwd) {
160
157
  return {};
161
158
  }
162
159
  }
163
- /** Does this tree's Makefile declare `target`? */
160
+ /**
161
+ * Does this tree's Makefile declare `target`? Reads the literal name `Makefile`
162
+ * only — unlike {@link readLaunchManifest}, this one does not walk make's lookup
163
+ * order, so a `GNUmakefile`-only tree answers false.
164
+ */
164
165
  export function makeHasTarget(cwd, target) {
165
166
  try {
166
167
  const mk = readFileSync(path.join(cwd, 'Makefile'), 'utf8');
@@ -3,24 +3,26 @@
3
3
  * `.pi-tasks/` (contracts, launch-contract, env-notes, accept-debt, repair-queue,
4
4
  * requirements, requirements-owned).
5
5
  *
6
- * Six modules each kept their own copy of the same seven steps: read the file
7
- * (ANY error → ''), parse it into records, key the records, drop an incoming item
8
- * whose key is already present, cap to the newest MAX (oldest dropped), mkdir the
9
- * tasks dir, write the whole file back (`lines.join('\n') + '\n'`, plain
10
- * `writeFile`, NOT atomic), and swallow every fault a ledger is a sharpener or
11
- * an auditing aid, never a blocker of the phase or gate that calls it. What
12
- * genuinely varied per site is the DATA SHAPE (file name, cap, key, line format,
13
- * parser) and exactly one RULE what an append does when it adds nothing new
14
- * (see `onNoop`). Everything else here is the ritual, so a module that keeps a
15
- * ledger is an ADAPTER: it declares its ledger and calls read/append/write.
6
+ * The ritual is seven steps: read the file (ANY error ''), parse it into records,
7
+ * key the records, drop an incoming item whose key is already present, cap to the
8
+ * newest MAX (oldest dropped), mkdir the tasks dir, write the whole file back
9
+ * (`lines.join('\n') + '\n'`, plain `writeFile`, NOT atomic). Every fault is
10
+ * swallowed a ledger is a sharpener or an auditing aid, never a blocker of the
11
+ * phase or gate that calls it.
12
+ *
13
+ * What varies per site is the DATA SHAPE (file name, cap, key, line format, parser)
14
+ * and exactly one RULE what an append does when it adds nothing new (see
15
+ * `onNoop`). Everything else here is the ritual, so a module that keeps a ledger is
16
+ * an ADAPTER: it declares its ledger and calls read/append/write.
16
17
  *
17
18
  * Contract details a caller can rely on:
18
19
  * • `readRaw` is the trimmed file text ('' when absent or unreadable). Prompt-block
19
20
  * builders take this string.
20
- * • `read` is `parse(readRaw)`; every parser skips blank lines and lines it cannot
21
- * read, so a corrupt line is dropped, never thrown on.
22
- * • `append` with an empty batch is a no-op (no read, no write). Within a batch
23
- * the first item with a key wins; a key already stored wins over the batch.
21
+ * • `read` is `parse(readRaw)`; every adapter's parser skips blank lines and lines
22
+ * it cannot read, so a corrupt line is dropped, never thrown on.
23
+ * • `append` with an empty batch is a no-op it does not even create `.pi-tasks/`.
24
+ * Within a batch the first item with a key wins; a key already stored wins over
25
+ * the batch.
24
26
  * • `write` overwrites with exactly these records; an empty list writes an empty
25
27
  * file (this is how a drained queue and a fully-resolved debt ledger look).
26
28
  * • Neither `append` nor `write` ever throws.
@@ -43,20 +43,18 @@ export interface LintFixDeps {
43
43
  */
44
44
  introducedBy?: (rel: string) => Promise<string | null>;
45
45
  /**
46
- * Debug-log sink. This dep did not exist, so all four of this pass's guard
47
- * trips were invisible in the trail while the twin (`FinalFixDeps.log`) logged
48
- * three of its own — and a guard whose firing leaves no record cannot be
49
- * distinguished from one that never armed.
46
+ * Debug-log sink. Every guard below logs its own trip through it: a guard whose
47
+ * firing leaves no record cannot be distinguished from one that never armed.
50
48
  */
51
49
  log?: (msg: string) => void;
52
50
  }
53
51
  /** The fix child edits and runs the checker; bash exists to RUN the check, not git. */
54
52
  export declare const LINT_FIX_TOOLS = "read,edit,bash";
55
53
  /**
56
- * Build the fix child's prompt. The constraints encode both live findings: smallest
57
- * edits only (the impl re-run's rewrite habit is the defect this pass replaces), and
58
- * an explicit ban on discarding work (both validation runs reached green via
59
- * `git checkout` of the work file until the guard existed).
54
+ * Build the fix child's prompt. Two constraints carry the design: smallest edits
55
+ * only the re-run's rewrite habit is the defect this pass replaces and an
56
+ * explicit ban on discarding work, which is the cheapest route to a green check and
57
+ * exactly what the revert-guard exists to catch.
60
58
  */
61
59
  export declare function buildLintFixPrompt(failReason: string, frozenPaths?: string[]): string;
62
60
  /**
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * lint-fix — the bounded, graduated resolution for a repo-health verify FAIL.
3
3
  *
4
- * The failure this closes (mx5 run 3, TASK_0017): AUTOFIX's only hammer is a FULL
5
- * implementation re-run. For a repo-health FAIL of 10 trivial lint findings the live
6
- * run burned two 36–56-minute impl turns, each REGENERATING a fresh 900-line rewrite
7
- * that failed lint differently the loop cannot converge because the tool is bigger
8
- * than the defect. Validated live on the real TASK_0017 tree: a bounded fix child
9
- * (read,edit,bash) reached lint-clean in 64s and 106s, 2/2.
4
+ * WHY A SEPARATE PASS. AUTOFIX's only hammer is a FULL implementation re-run. For a
5
+ * repo-health FAIL of a few trivial lint findings the re-run REGENERATES the work
6
+ * rather than editing it, and fresh output fails lint differently the loop cannot
7
+ * converge because the tool is bigger than the defect. A bounded fix child
8
+ * (read,edit,bash) edits only the findings.
10
9
  *
11
- * The same validation caught the design's failure mode: BOTH runs cheated, running
12
- * `git checkout -- src/test/request.ts` REVERTING the task's uncommitted work to
13
- * make the findings vanish. So this pass ships with a deterministic REVERT-GUARD,
14
- * outcome-based (command filtering can't catch every path to the same effect):
10
+ * REVERT-GUARD. A fix child can reach lint-clean by DISCARDING the task's
11
+ * uncommitted work: `git checkout -- <the work file>` makes every finding vanish.
12
+ * The prompt forbids it, but the guard is outcome-based, because command filtering
13
+ * cannot catch every path to the same effect:
15
14
  *
16
15
  * - Before the child runs: snapshot the full working state (`git add -A` +
17
16
  * `git write-tree`, then unstage) and record which files differ from HEAD.
@@ -28,34 +27,38 @@
28
27
  * recommend → AUTOFIX/ACCEPT/dismiss picker, so this pass can only make the loop
29
28
  * faster, never change what it can decide.
30
29
  *
31
- * FROZEN-PATH GUARD (mx5 run 12, TASK_0021/0022): the checker's own error text
32
- * can INSTRUCT an edit to a spec-frozen file (typed ESLint: "playwright/index.ts
33
- * was not found by the project … Consider either including it in the
34
- * tsconfig.json") and this child complies — while the task's spec froze
35
- * `tsconfig.json` and verify's rule-4b prohibition probe then fails the TASK for
36
- * the gate child's edit. Two gates, contradictory rules, same file; the loop
37
- * never converges (live: TASK_0021 burned all three unattended AUTOFIX rounds).
38
- * Prompt framing alone is A/B-proven ~0–1/5 on the weak model (see
39
- * frozen-path-guard.ts), so the deny is mechanical: the spec's frozen paths are
30
+ * FROZEN-PATH GUARD. The checker's own error text can INSTRUCT an edit to a
31
+ * spec-frozen file. Typed ESLint, asked to lint a file outside the tsconfig
32
+ * `include`, prints:
33
+ *
34
+ * Parsing error: <file> was not found by the project service. Consider either
35
+ * including it in the tsconfig.json or including it in allowDefaultProject.
36
+ *
37
+ * A child that complies edits `tsconfig.json`. If the task's spec froze that file,
38
+ * verify's prohibition probe raises rule 4b "SPEC PROHIBITIONS ARE PART OF THE
39
+ * BAR — YOU HAVE NO WAIVER AUTHORITY" — and FAILs the TASK for the gate child's
40
+ * edit. Two gates, contradictory rules, same file; the loop never converges.
41
+ *
42
+ * So the deny is mechanical rather than framing: the spec's frozen paths are
40
43
  * threaded in via `frozenPaths`, injected into the prompt as a do-not-touch list
41
- * (belt), and any frozen path the child still changed is deterministically
42
- * reverted post-child and the fix reported not-applied (suspenders). Only paths
43
- * that were CLEAN before the child ran are reverted — a frozen path already
44
- * dirty with (possibly task) work is left alone, in the guard's safe direction:
45
- * cost time, never work.
44
+ * (belt), and any frozen path the child still changed is deterministically reverted
45
+ * post-child and the fix reported not-applied (suspenders). Only paths that were
46
+ * CLEAN before the child ran are reverted — a frozen path already dirty with
47
+ * (possibly task) work is left alone, in the guard's safe direction: cost time,
48
+ * never work.
46
49
  *
47
- * CROSS-TASK DELETION GUARD (mx5 run 12 PROMPT 2, verify-debug.log 17:53:44): the
48
- * revert-guard above is outcome-based but blind to a CLEAN tracked file a
49
- * sibling task's committed deliverable is neither pre-dirty nor pre-untracked, so
50
- * DELETING it slips both checks, and if the lint converges the pass returns ok
51
- * (confirmed live: the child deleted TASK_0020's playwright ct files to green a
52
- * typed-lint it was frozen out of fixing properly). The discriminator is
53
- * PROVENANCE, not deletion per se: a tracked file the CHILD deleted whose
54
- * introducing task (per `introducedBy`, git history) differs from the CURRENT
50
+ * CROSS-TASK DELETION GUARD. The revert-guard above is outcome-based but blind to a
51
+ * CLEAN tracked file. Delete one and `git status --porcelain` reports ` D <path>`
52
+ * while it appears in NEITHER of the guard's two lists: it was not pre-dirty (it
53
+ * matched HEAD) and not pre-untracked (it is tracked). If the lint then converges,
54
+ * the pass returns ok having destroyed a sibling task's committed deliverable. The
55
+ * discriminator is PROVENANCE, not deletion per se: a tracked file the CHILD deleted
56
+ * whose introducing task (per `introducedBy`, git history) differs from the CURRENT
55
57
  * task is restored from HEAD and the fix reported not-applied naming the owner.
56
- * Same-task deletions, unknown provenance, relocations, and any git error all
57
- * step aside the guard may only cost time, never work. Armed only when both
58
- * `currentTaskId` and `introducedBy` are wired.
58
+ * Same-task deletions, unknown provenance, relocations (a deletion whose basename
59
+ * reappears among the added paths) and any git error all step aside the guard may
60
+ * only cost time, never work. Armed only when both `currentTaskId` and
61
+ * `introducedBy` are wired.
59
62
  */
60
63
  import { parseChangedFrozenFiles, pathNamedIn, revertFrozenPaths } from './frozen-path-guard.js';
61
64
  import { runFixChild } from './fix-child.js';
@@ -64,10 +67,10 @@ import { findCrossTaskDeletions } from './task-provenance.js';
64
67
  /** The fix child edits and runs the checker; bash exists to RUN the check, not git. */
65
68
  export const LINT_FIX_TOOLS = 'read,edit,bash';
66
69
  /**
67
- * Build the fix child's prompt. The constraints encode both live findings: smallest
68
- * edits only (the impl re-run's rewrite habit is the defect this pass replaces), and
69
- * an explicit ban on discarding work (both validation runs reached green via
70
- * `git checkout` of the work file until the guard existed).
70
+ * Build the fix child's prompt. Two constraints carry the design: smallest edits
71
+ * only the re-run's rewrite habit is the defect this pass replaces and an
72
+ * explicit ban on discarding work, which is the cheapest route to a green check and
73
+ * exactly what the revert-guard exists to catch.
71
74
  */
72
75
  export function buildLintFixPrompt(failReason, frozenPaths = []) {
73
76
  const frozenBlock = frozenPaths.length === 0 ?
@@ -123,7 +126,7 @@ export function revertGuardViolations(preDirty, stillDirty) {
123
126
  const EXCLUDE_TASKS_DIR = ':(exclude).pi-tasks';
124
127
  /**
125
128
  * Files differing from HEAD, or null when git itself failed. The distinction is
126
- * load-bearing: mx5 run 4 rolled back two GOOD converged fixes because a git
129
+ * load-bearing: without it a run rolls back GOOD converged fixes because a git
127
130
  * failure after the child read as "[] files still dirty" → every pre-existing work
128
131
  * file looked reverted → false "discarded work". A git error is INCONCLUSIVE, not
129
132
  * evidence of destruction.
@@ -205,12 +208,10 @@ export async function runBoundedLintFix(deps) {
205
208
  // guard for this run (inconclusive ≠ evidence).
206
209
  const deletionGuardArmed = Boolean(deps.currentTaskId && deps.introducedBy);
207
210
  const preChanges = deletionGuardArmed ? await treeChanges(deps) : null;
208
- // The four-rung ladder lives in task/fix-child.ts. A cancel still propagates
209
- // (it THROWS, so the caller's USER_CANCELLED path is unchanged); what is new
210
- // here is the BLOCKED rung, which this pass instructed in its prompt and then
211
- // discarded the reply of — so a child that reported BLOCKED still paid the
212
- // whole guard stack and a repo-health re-run (15–69s measured) to be told
213
- // `did not converge`, instead of its own stated reason.
211
+ // The four-rung ladder lives in task/fix-child.ts: a cancel propagates as a
212
+ // THROW (so the caller's USER_CANCELLED path is unchanged), a child that threw
213
+ // for any other reason is `error`, a self-declared BLOCKED is `blocked`, and
214
+ // anything else is `done`.
214
215
  const end = await runFixChild({
215
216
  runChild: deps.runChild,
216
217
  tools: LINT_FIX_TOOLS,
@@ -228,10 +229,10 @@ export async function runBoundedLintFix(deps) {
228
229
  // where the twin consults its own.
229
230
  // REVERT-GUARD: every pre-existing work file must still differ from HEAD, and
230
231
  // every pre-existing untracked file must still exist. Trip → restore snapshot.
231
- // Every comparison requires git to have actually SUCCEEDED: a git error after
232
- // the child is inconclusive (proven live: it flagged the whole untracked file
233
- // list as "discarded" while the child had verifiably edited only lint findings)
234
- // — then the guard steps aside and the converge check below still decides.
232
+ // Every comparison requires git to have actually SUCCEEDED. A git error after
233
+ // the child reads as "nothing is dirty", which would flag every pre-existing
234
+ // work file as discarded inconclusive, not evidence. The guard then steps
235
+ // aside and the converge check below still decides.
235
236
  const stillDirtyList = await dirtyFiles(deps);
236
237
  let guardNote;
237
238
  const violations = [];
@@ -271,10 +272,9 @@ export async function runBoundedLintFix(deps) {
271
272
  }
272
273
  // CROSS-TASK DELETION GUARD: a tracked file the CHILD deleted whose
273
274
  // introducing task differs from the current task is a sibling's committed
274
- // deliverable destroyed to go green (mx5 run 12: the child deleted TASK_0020's
275
- // playwright ct files and the pass returned ok the revert-guard above only
276
- // watches pre-dirty and pre-untracked files, and a clean tracked sibling file
277
- // is neither). Restore JUST those paths from HEAD (they were clean pre-child,
275
+ // deliverable destroyed to go green. The revert-guard above only watches
276
+ // pre-dirty and pre-untracked files, and a clean tracked sibling file is
277
+ // neither. Restore JUST those paths from HEAD (they were clean pre-child,
278
278
  // so nothing of the task's work can be lost) and report not-applied naming the
279
279
  // owner. Inconclusive on any side — git error, unknown provenance, same-task
280
280
  // deletion, relocation — steps aside.
@@ -300,14 +300,13 @@ export async function runBoundedLintFix(deps) {
300
300
  }
301
301
  }
302
302
  }
303
- // FROZEN-PATH GUARD: a frozen path that was clean pre-child and is changed
304
- // now is the child's edit — the exact write verify's rule-4b prohibition
305
- // probe is guaranteed to fail the TASK for (mx5 run 12: ESLint's own error
306
- // text instructed the tsconfig.json edit and the child complied, 5/5 in the
307
- // live A/B). Revert JUST those paths (they were clean, so HEAD == pre-state:
308
- // no work can be lost) and report not-applied so the caller falls through to
309
- // the ordinary recommend → AUTOFIX/ACCEPT picker. Inconclusive git (either
310
- // side) → guard steps aside; the verify probe still catches what survives.
303
+ // FROZEN-PATH GUARD: a frozen path that was clean pre-child and is changed now
304
+ // is the child's edit — the exact write verify's rule-4b prohibition probe
305
+ // fails the TASK for. Revert JUST those paths (they were clean, so HEAD ==
306
+ // pre-state: no work can be lost) and report not-applied so the caller falls
307
+ // through to the ordinary recommend AUTOFIX/ACCEPT picker. Inconclusive git
308
+ // (either side) guard steps aside; the verify probe still catches what
309
+ // survives.
311
310
  if (preFrozenDirty !== null) {
312
311
  const postFrozenDirty = await frozenDirtySet(deps, frozen);
313
312
  if (postFrozenDirty !== null) {
@@ -341,18 +340,17 @@ export async function runBoundedLintFix(deps) {
341
340
  // while the child's edits sit in the working tree.
342
341
  //
343
342
  // So BLOCKED does not decide; it only supplies a better REASON when the check
344
- // agrees nothing converged. That was the durable half of the win. Skipping the
345
- // re-run was the other half, and it is not worth this.
343
+ // agrees nothing converged. Skipping the re-run on the marker alone is not
344
+ // worth that risk.
346
345
  const health = await deps.repoHealth();
347
346
  if (!health.ok) {
348
347
  if (end.kind === 'blocked') {
349
348
  return { ok: false, reason: `fix child blocked: ${end.note}` };
350
349
  }
351
- // FROZEN-PATH TRACE on non-convergence (PROMPT 1 layer B): when the child
352
- // was honest — it did NOT touch the frozen path, so the guard above never
353
- // tripped — but the check is still red and its own output NAMES a frozen
354
- // path (typed ESLint: "playwright/index.ts was not found by the project
355
- // consider including it in the tsconfig.json"), the findings can only be
350
+ // FROZEN-PATH TRACE on non-convergence: when the child was honest it did
351
+ // NOT touch the frozen path, so the guard above never tripped — but the
352
+ // check is still red and its own output NAMES a frozen path (the typed
353
+ // ESLint message quoted in this file's header), the findings can only be
356
354
  // fixed by an edit this task's spec forbids. Report it under the same
357
355
  // `frozen-path:` prefix as the guard trip, so the gate loop can route
358
356
  // straight to the human picker instead of burning unattended AUTOFIX
@@ -12,7 +12,19 @@
12
12
  *
13
13
  * Either pattern returns a LoopHit so the caller can kill the child and re-spawn
14
14
  * with a hint. No I/O. No imports from index.ts. Trivially unit-testable.
15
+ *
16
+ * The three tuning constants live here rather than in child-runner.ts because
17
+ * worker-profiles.ts reads them at module top level to build DEFAULT_LOOP_DETECTOR.
18
+ * From child-runner.ts that is a cycle — child-runner → worker-profiles →
19
+ * child-runner — and the failure is a TDZ ReferenceError on import order, which
20
+ * no compile step catches. This module imports nothing, so it cannot close one.
15
21
  */
22
+ /** Recent tool calls the exact-repeat rule looks back over. */
23
+ export declare const LOOP_WINDOW = 20;
24
+ /** Repeats of one key within the window that trip the detector. */
25
+ export declare const LOOP_THRESHOLD = 5;
26
+ /** Re-spawns allowed after a loop kill — 3 attempts total with the initial one. */
27
+ export declare const MAX_LOOP_RESTARTS = 2;
16
28
  export interface ToolCall {
17
29
  name: string;
18
30
  args: unknown;
@@ -27,6 +39,12 @@ export interface LoopHit {
27
39
  * Arrays preserve their order (positional). undefined / primitives passthrough.
28
40
  */
29
41
  export declare function stableStringify(value: unknown): string;
42
+ /**
43
+ * The exact-match identity of a tool call. Exported because implementation-guards.ts
44
+ * keys its own per-call strike count on it, and a second hand-written copy of this
45
+ * expression is free to drift away from the one `record` uses.
46
+ */
47
+ export declare function loopKey(call: ToolCall): string;
30
48
  /**
31
49
  * The primary file path a tool call targets, mirroring summarizeToolArgs' field
32
50
  * precedence. Returns null when the call names no path (e.g. bash, or a grep
@@ -47,15 +65,16 @@ export declare class LoopDetector {
47
65
  /**
48
66
  * Count same-path calls in the window that are "revisits" — accesses that end
49
67
  * no further into the file than the furthest line already covered for that
50
- * path. Paging yields zero revisits and never trips; repeated whole-file
51
- * re-reads, backward jumps and path-targeting greps accumulate.
68
+ * path. The FIRST call on a path is therefore never a revisit: it sets the
69
+ * high-water mark. Paging yields zero revisits and never trips; repeated
70
+ * whole-file re-reads, backward jumps, narrower re-reads and repeated
71
+ * path-targeting greps all accumulate.
52
72
  *
53
- * The comparison is on the RANGE, not the offset. Offset alone scored a page
54
- * that re-asks from the same start with a bigger limit `{offset:80,
55
- * limit:400}` after `{offset:80, limit:300}`, real reads from a measured
56
- * decompose run as a revisit, even though it covers 100 lines the child had
57
- * never seen. That is the same mistake SingleReadGuard made by keying on the
58
- * path alone, and it is corrected the same way.
73
+ * The comparison is on the RANGE, not the offset. Keying on offset alone scores
74
+ * `{offset:80, limit:400}` after `{offset:80, limit:300}` as a revisit, even
75
+ * though it covers 100 lines the child has never seen. That is the same mistake
76
+ * SingleReadGuard made by keying on the path alone, and it is corrected the same
77
+ * way.
59
78
  */
60
79
  private countRevisits;
61
80
  }