@mjasnikovs/pi-task 0.38.28 → 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 +25 -7
  104. package/dist/task/context-usage.js +21 -6
  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 +37 -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 +180 -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 +33 -36
  231. package/dist/task/research-worker.js +39 -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 +86 -54
  338. package/dist/workers/pi-worker-core.js +112 -112
  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 +23 -10
  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,32 +1,23 @@
1
1
  /**
2
2
  * Running ONE bounded fix child and deciding what its ending MEANS.
3
3
  *
4
- * Two graduated-resolution passes drive a fix child through the same four rungs —
5
- * cancel propagates, a thrown child is an error, a self-declared BLOCKED ends the
6
- * attempt, anything else is DONE and `final-gate-fix.ts:12` says so out loud:
7
- * it "mirrors the per-task graduated-resolution shape (lint-fix.ts)". This is that
8
- * ladder, once.
4
+ * Two graduated-resolution passes `lint-fix.ts` and `final-gate-fix.ts` drive
5
+ * a fix child through the same four rungs: a cancel propagates as a throw, a
6
+ * child that threw for any other reason is an `error`, a self-declared BLOCKED is
7
+ * `blocked`, and anything else is `done`. `final-gate-fix.ts`'s own header says
8
+ * it "mirrors the per-task graduated-resolution shape (lint-fix.ts)". This is
9
+ * that ladder, written once.
9
10
  *
10
- * NOT the rejected sharing. CONTEXT.md's "the two resolution loops stay two" is
11
- * about `runGatesForTask` vs `runFinalGateStage` the LOOPS, at two altitudes.
12
- * This is one altitude down: the single child invocation inside each, where the
13
- * two had actually drifted.
11
+ * Both prompts instruct the marker their pass parses: `LINT-FIX: DONE` /
12
+ * `LINT-FIX: BLOCKED <why>` and `FINAL-GATE-FIX: DONE` / `FINAL-GATE-FIX:
13
+ * BLOCKED <why>`.
14
14
  *
15
- * - **The lint-fix marker was a DEAD protocol.** `buildLintFixPrompt` instructs
16
- * the child to end with `LINT-FIX: DONE` or `LINT-FIX: BLOCKED <why>`, and the
17
- * call site was `await deps.runChild(...)` with the return value DISCARDED
18
- * nothing in `src/` or `scripts/` parsed it. The twin parsed its own marker and
19
- * used it to skip the expensive gate re-run. So a lint-fix child that reported
20
- * BLOCKED still paid the full guard stack plus a whole repo-health run (15–69s
21
- * measured), and the user was told `did not converge: <health.reason>` instead
22
- * of the child's own stated reason. The suite fed `'LINT-FIX: DONE'` as fake
23
- * output, so it stayed green whether the marker was parsed or deleted.
24
- * - **The cancel rung re-typed its constant.** `lint-fix.ts` compared against a
25
- * literal `'__user_cancelled__'`; it was the only production site in `src/` not
26
- * importing `USER_CANCELLED`.
27
- *
28
- * What stays per-site: the arbiter (a gate re-run vs a repo-health re-run), the
29
- * result shape, and the guard sets. Only the child call is shared.
15
+ * What stays per-site: the arbiter, the result shape, and the guard sets. Only
16
+ * the child call is shared. The arbiters differ in kind `final-gate-fix` calls
17
+ * `deps.gate`, `lint-fix` calls `deps.repoHealth` and so does what BLOCKED
18
+ * buys: `final-gate-fix` skips its gate re-run on a blocked child, while
19
+ * `lint-fix` runs repo-health regardless and uses the marker only to word the
20
+ * failure.
30
21
  */
31
22
  /** How one fix child ended. A CANCEL is not a member: it throws, so the caller's
32
23
  * own `USER_CANCELLED` path runs unchanged. */
@@ -52,9 +43,12 @@ export interface FixChildInput {
52
43
  }
53
44
  /**
54
45
  * Parse a fix child's final marker. Last match wins: the model reasons before
55
- * concluding, and bash output can echo the words. No marker DONE, because the
56
- * pass's own arbiter re-runs either way; a missing marker only forfeits the
57
- * early-out on a self-declared BLOCKED.
46
+ * concluding, and both passes give the child `bash`, so its own command output is
47
+ * in the text being scraped.
48
+ *
49
+ * No marker → DONE. Neither pass treats the marker as the verdict — the arbiter
50
+ * decides — so a missing marker costs at most the blocked early-out in
51
+ * `final-gate-fix`, and nothing at all in `lint-fix`.
58
52
  */
59
53
  export declare function parseFixMarker(marker: string, text: string): {
60
54
  blocked: boolean;
@@ -1,39 +1,33 @@
1
1
  /**
2
2
  * Running ONE bounded fix child and deciding what its ending MEANS.
3
3
  *
4
- * Two graduated-resolution passes drive a fix child through the same four rungs —
5
- * cancel propagates, a thrown child is an error, a self-declared BLOCKED ends the
6
- * attempt, anything else is DONE and `final-gate-fix.ts:12` says so out loud:
7
- * it "mirrors the per-task graduated-resolution shape (lint-fix.ts)". This is that
8
- * ladder, once.
4
+ * Two graduated-resolution passes `lint-fix.ts` and `final-gate-fix.ts` drive
5
+ * a fix child through the same four rungs: a cancel propagates as a throw, a
6
+ * child that threw for any other reason is an `error`, a self-declared BLOCKED is
7
+ * `blocked`, and anything else is `done`. `final-gate-fix.ts`'s own header says
8
+ * it "mirrors the per-task graduated-resolution shape (lint-fix.ts)". This is
9
+ * that ladder, written once.
9
10
  *
10
- * NOT the rejected sharing. CONTEXT.md's "the two resolution loops stay two" is
11
- * about `runGatesForTask` vs `runFinalGateStage` the LOOPS, at two altitudes.
12
- * This is one altitude down: the single child invocation inside each, where the
13
- * two had actually drifted.
11
+ * Both prompts instruct the marker their pass parses: `LINT-FIX: DONE` /
12
+ * `LINT-FIX: BLOCKED <why>` and `FINAL-GATE-FIX: DONE` / `FINAL-GATE-FIX:
13
+ * BLOCKED <why>`.
14
14
  *
15
- * - **The lint-fix marker was a DEAD protocol.** `buildLintFixPrompt` instructs
16
- * the child to end with `LINT-FIX: DONE` or `LINT-FIX: BLOCKED <why>`, and the
17
- * call site was `await deps.runChild(...)` with the return value DISCARDED
18
- * nothing in `src/` or `scripts/` parsed it. The twin parsed its own marker and
19
- * used it to skip the expensive gate re-run. So a lint-fix child that reported
20
- * BLOCKED still paid the full guard stack plus a whole repo-health run (15–69s
21
- * measured), and the user was told `did not converge: <health.reason>` instead
22
- * of the child's own stated reason. The suite fed `'LINT-FIX: DONE'` as fake
23
- * output, so it stayed green whether the marker was parsed or deleted.
24
- * - **The cancel rung re-typed its constant.** `lint-fix.ts` compared against a
25
- * literal `'__user_cancelled__'`; it was the only production site in `src/` not
26
- * importing `USER_CANCELLED`.
27
- *
28
- * What stays per-site: the arbiter (a gate re-run vs a repo-health re-run), the
29
- * result shape, and the guard sets. Only the child call is shared.
15
+ * What stays per-site: the arbiter, the result shape, and the guard sets. Only
16
+ * the child call is shared. The arbiters differ in kind `final-gate-fix` calls
17
+ * `deps.gate`, `lint-fix` calls `deps.repoHealth` and so does what BLOCKED
18
+ * buys: `final-gate-fix` skips its gate re-run on a blocked child, while
19
+ * `lint-fix` runs repo-health regardless and uses the marker only to word the
20
+ * failure.
30
21
  */
31
22
  import { USER_CANCELLED } from './child-runner.js';
32
23
  /**
33
24
  * Parse a fix child's final marker. Last match wins: the model reasons before
34
- * concluding, and bash output can echo the words. No marker DONE, because the
35
- * pass's own arbiter re-runs either way; a missing marker only forfeits the
36
- * early-out on a self-declared BLOCKED.
25
+ * concluding, and both passes give the child `bash`, so its own command output is
26
+ * in the text being scraped.
27
+ *
28
+ * No marker → DONE. Neither pass treats the marker as the verdict — the arbiter
29
+ * decides — so a missing marker costs at most the blocked early-out in
30
+ * `final-gate-fix`, and nothing at all in `lint-fix`.
37
31
  */
38
32
  export function parseFixMarker(marker, text) {
39
33
  const re = new RegExp(`${marker}:\\s*(DONE|BLOCKED)\\b[ \\t]*(.*)`, 'gi');
@@ -18,8 +18,9 @@ export interface ForeignPathFinding {
18
18
  * `src/client/api.ts`). Returns the resolved repo-relative path, or null.
19
19
  *
20
20
  * This mirrors how the tools that CONSUME these paths (vite/tsconfig aliases,
21
- * bundler resolvers) look them up without it the mx5 true positive
22
- * `/workspace/src/client/api` would be missed, since only `src/client/api.ts` exists.
21
+ * bundler resolvers) look them up. Without it an extensionless leak is missed:
22
+ * measured here, `src/task/foreign-path` resolves to `src/task/foreign-path.ts`,
23
+ * and `src/nope` resolves to null.
23
24
  */
24
25
  export declare function resolveRepoPath(rel: string, exists: (rel: string) => boolean): string | null;
25
26
  /**
@@ -30,9 +31,9 @@ export declare function resolveRepoPath(rel: string, exists: (rel: string) => bo
30
31
  * synthetic tree.
31
32
  *
32
33
  * The tail search strips the FEWEST leading segments that still resolve, so the
33
- * finding names the smallest foreign prefix (`/workspace`, not `/workspace/src`)
34
- * and the largest repo-relative target — the reading that matches how a mount
35
- * actually shadows a tree.
34
+ * finding names the smallest foreign prefix and the largest repo-relative target —
35
+ * the reading that matches how a mount shadows a tree. Measured:
36
+ * `/aaa/bbb/src/task/foreign-path.ts` reports prefix `/aaa/bbb`, not `/aaa`.
36
37
  */
37
38
  export declare function findForeignPaths(lines: AddedLine[], existsOnHost: (abs: string) => boolean, existsInRepo: (rel: string) => boolean): ForeignPathFinding[];
38
39
  /**
Binary file
@@ -1,6 +1,6 @@
1
1
  /** One unsatisfiable freeze/requires-edit pair found in a composed spec. */
2
2
  export interface FrozenPathConflict {
3
- /** The frozen path (normalized: no leading ./, no trailing /). */
3
+ /** The frozen path (normalized: no leading./, no trailing /). */
4
4
  path: string;
5
5
  /** The freeze line, verbatim (the `Do NOT modify` constraint). */
6
6
  constraint: string;
@@ -20,17 +20,16 @@ export interface FrozenPathConflict {
20
20
  * SPEC alone; research contributes only the statement side (live compose
21
21
  * sometimes drops the research's "must also be included" nuance from the spec
22
22
  * text while shipping the freeze and the file creation - the contradiction is
23
- * then visible only across the compose boundary). Deterministic, pure text -
24
- * the same extraction the live A/B grounds its measurements in, so the model
25
- * cannot self-report its way past it. Empty on a null spec or one that froze
26
- * nothing.
23
+ * then visible only across the compose boundary). Deterministic and pure text,
24
+ * so the model cannot self-report its way past it. Empty on a null spec or one
25
+ * that froze nothing.
27
26
  */
28
27
  export declare function findFrozenPathConflicts(spec: string | null | undefined, research?: string | null): FrozenPathConflict[];
29
28
  /**
30
- * The forced critique-rewrite defect text (skip-escape pattern: MANDATORY,
31
- * self-contained, names the exact resolution options). The two permitted
32
- * resolutions come straight from the incident analysis: scoped ownership or
33
- * dropping the creation prose acknowledging the gap is called out as a
34
- * non-resolution because that is precisely what the live model shipped.
29
+ * The forced critique-rewrite defect text: MANDATORY, self-contained, and it
30
+ * names the exact resolution options. Only two are permitted — scoped ownership
31
+ * or dropping the creation — and prose acknowledging the gap is called out as a
32
+ * non-resolution, because that is the shape a model reaches for first and it
33
+ * leaves the contradiction intact.
35
34
  */
36
35
  export declare function frozenConflictProbeText(conflicts: FrozenPathConflict[]): string;
@@ -1,54 +1,50 @@
1
1
  /**
2
2
  * frozen-conflict — deterministic detection of an UNSATISFIABLE spec pair at
3
- * compose time (mx5 run 12 root cause, PROMPT 1 layer A).
3
+ * compose time.
4
4
  *
5
- * The failure this closes: compose authored TASK_0020 with a blanket freeze
6
- * ("Do not modify `tsconfig.json`, `eslint.config.js`, or `.prettierrc.cjs`;
7
- * those are handled in steps 1–2") while the deliverable new
8
- * `playwright-ct.config.ts` + `playwright/index.ts` needs exactly that
9
- * registration edit, and the spec "resolved" the contradiction with prose
10
- * surrender ("Accept that `playwright-ct.config.ts` will not be covered by
11
- * `tsc --noEmit` since `tsconfig.json` is not modified in this step"). Typed
12
- * ESLint does not "accept" anything: the moment the created files land,
13
- * `bun run lint` hard-errors REPO-WIDE and permanently — and the "owning"
14
- * steps 1–2 completed long ago, so NO task may ever perform the edit (every
15
- * spec carries the same freeze). Every subsequent task then burned its
16
- * unattended AUTOFIX rounds on a repo-health FAIL none of them was allowed to
17
- * fix, and the eventual "escape" was a child DELETING the deliverables.
5
+ * The shape: a spec blanket-freezes a config file while its own deliverable
6
+ * needs exactly the registration edit that freeze forbids, and the
7
+ * contradiction is "resolved" in prose ("Accept that it will not be covered by
8
+ * `tsc --noEmit` since `tsconfig.json` is not modified in this step"). A typed
9
+ * linter accepts nothing. The moment the created files land the repo-wide
10
+ * static check fails PERMANENTLY, and because the step that owns the frozen
11
+ * file has already completed, no later task is allowed to fix it either — every
12
+ * one of them burns its autofix rounds on a defect none of them may touch.
18
13
  *
19
- * The contradiction is visible IN THE COMPOSED TEXT ITSELF, so it must die at
20
- * spec time. Like the skip-escape / synthesized-wiring / plan-contradiction
21
- * probes (prompt-only rules are A/B-proven ~0–1/5 on the weak model), the
22
- * detector is deterministic and its finding is FORCED into the critique
23
- * rewrite: the rewrite must either grant scoped ownership ("MAY edit `X` ONLY
24
- * to register the files this task creates") or drop the file creation.
14
+ * The contradiction is visible IN THE COMPOSED TEXT ITSELF, so it dies at spec
15
+ * time: the detector is deterministic and its finding is FORCED into the
16
+ * critique rewrite, which must either grant scoped ownership ("MAY edit `X`
17
+ * ONLY to register the files this task creates") or drop the file creation.
18
+ *
19
+ * High-precision by construction. Each rule below was run against the detector:
25
20
  *
26
- * High-precision by construction (FP-swept over the 26 real mx5 run-12 specs):
27
21
  * - the freeze side must be BLANKET — a `Do NOT modify` line with no
28
- * exception clause; a scoped freeze ("MAY edit ONLY to register",
29
- * "Only edit `X` ", "… except to add …") is already the resolution shape
30
- * and must never re-fire;
31
- * - the statement side is judged per SENTENCE, not per line (real GOALs are
32
- * one giant line line scoping false-fired on `must include` in a
33
- * response-shape sentence three paths away from the frozen one);
34
- * - a sentence that is itself prohibition-shaped ("Preserve do not
35
- * remove or modify …") is the freeze side restated, never a statement;
36
- * - the sentence must NAME the frozen path (pathNamedIn) AND match one of
37
- * the measured phrasing families: passive registration ("must also be
38
- * included"), unless-added ("won't be type-checked unless added"),
39
- * directional active ("requires adding to" / "must add to"), or the
40
- * prose SURRENDER itself ("will not be covered … since `X` is not
41
- * modified" the exact line the live TASK_0020 spec shipped).
42
- * Mere co-mention never fires.
22
+ * exception clause. A scoped freeze is already the resolution shape and
23
+ * must never re-fire; "… except to add …", "You MAY edit `X` ONLY to
24
+ * register …" and "Only edit `X` (…)" each yield zero against a statement
25
+ * that otherwise fires;
26
+ * - the statement side is judged per SENTENCE, not per line, because real
27
+ * GOALs are one giant line. Measured: a line carrying the GOAL, a
28
+ * response-shape "must include `id` and `name`", and a path mention yields
29
+ * nothing, while the same line shape carrying a real registration sentence
30
+ * yields one;
31
+ * - a prohibition-shaped sentence ("Preserve do not remove or modify …") is
32
+ * the freeze side restated and yields nothing;
33
+ * - the sentence must NAME the frozen path AND match one of the phrasing
34
+ * families. All six fire: passive registration, unless-added,
35
+ * not-checked-unless, "requires adding to", "must add to", and the
36
+ * surrender pair.
37
+ *
38
+ * Mere co-mention never fires: "The build reads `tsconfig.json` at startup."
39
+ * yields nothing.
43
40
  */
44
41
  import { extractProhibitions, PROHIBITION_RE } from './prohibition-probe.js';
45
42
  import { pathNamedIn } from './frozen-path-guard.js';
46
43
  /**
47
44
  * A freeze that carves out its own exception is SCOPED, not blanket — it is
48
- * exactly the resolution shape the probe demands ("MAY edit `X` ONLY to
49
- * register…", "Only edit `X` (add the search route)…", "Do not modify `X`
50
- * except to add…"), so it never counts as the freeze side of a conflict, or
51
- * the recomposed spec would re-fire forever.
45
+ * exactly the resolution shape the probe demands, so it never counts as the
46
+ * freeze side of a conflict. Without this the recomposed spec would re-fire
47
+ * forever on its own fix.
52
48
  */
53
49
  const EXCEPTION_RE = /\b(?:except|unless|beyond|other\s+than|apart\s+from|only\s+(?:to|for|if|when|where|edit|modify|change|touch|update)|may\s+(?:edit|modify|change|update|add))\b/i;
54
50
  /** Passive registration: "must (also) be included/added/registered/…". */
@@ -58,13 +54,13 @@ const UNLESS_ADDED_RE = /\bunless\s+(?:it\s+is\s+|it'?s\s+|they\s+are\s+|first\s
58
54
  /** "won't be type-checked/compiled/linted/covered … unless …". */
59
55
  const NOT_CHECKED_UNLESS_RE = /\b(?:won'?t|will\s+not|cannot|can'?t|does\s+not|doesn'?t|is\s+not|isn'?t)\s+(?:be\s+)?[\w\s-]{0,40}?(?:type-?check|compil|lint|cover|resolv|recogni[sz]|includ|pick)\w*[^;]{0,80}?\bunless\b/i;
60
56
  /** Directional active: "requires adding … to …" / "must add … to/into/in …".
61
- * The preposition requirement is what keeps a response-shape "must include
62
- * `field`" sentence from counting as a registration edit. */
57
+ * The preposition is what keeps a response-shape sentence from counting as a
58
+ * registration edit measured, "The response must include `tsconfig.json`."
59
+ * yields nothing while "You must add the new entry to `tsconfig.json`." fires. */
63
60
  const REQUIRES_DIRECTIONAL_RE = /\brequires?\s+(?:add|includ|regist|updat|edit|modify|chang)\w*[^;]{0,80}?\b(?:to|into|in)\b/i;
64
61
  const MUST_ADD_DIRECTIONAL_RE = /\bmust\s+(?:also\s+)?(?:add|includ|regist|updat)\w*[^;]{0,80}?\b(?:to|into|in)\b/i;
65
62
  /** The prose-surrender pair: an artifact "will not be covered/type-checked/…"
66
- * BECAUSE the (frozen) path "is not modified". Both halves must be present
67
- * this is the exact contradiction shape the live TASK_0020 spec shipped. */
63
+ * BECAUSE the (frozen) path "is not modified". BOTH halves must be present. */
68
64
  const NOT_COVERED_RE = /\b(?:will\s+not|won'?t|cannot|can'?t|is\s+not|isn'?t)\s+(?:be\s+)?(?:covered|type-?checked|checked|compiled|linted|validated|included)\b/i;
69
65
  const BECAUSE_NOT_MODIFIED_RE = /\b(?:since|because|as)\b[^;]{0,80}?\b(?:is\s+|are\s+|was\s+|were\s+)?not\s+(?:be(?:ing)?\s+)?(?:modif|edit|chang|updat|touch)\w*/i;
70
66
  function isRequiresEditStatement(sentence) {
@@ -77,16 +73,18 @@ function isRequiresEditStatement(sentence) {
77
73
  }
78
74
  /**
79
75
  * Split a spec line into sentences: a `.` or `;` followed by whitespace and a
80
- * capital/backtick/bracket opener ends a sentence. The opener requirement
81
- * keeps `e.g. foo` and mid-path dots intact; backtick paths carry no `. ` so
82
- * they never split. Judging per sentence is what makes one-giant-line GOALs
83
- * scannable without cross-sentence false fires.
76
+ * capital, backtick or bracket opener ends a sentence. The opener requirement
77
+ * keeps `e.g. foo` and mid-path dots intact measured, a sentence containing
78
+ * "e.g." still matches as one piece. Judging per sentence is what makes
79
+ * one-giant-line GOALs scannable without cross-sentence false fires.
84
80
  */
85
81
  function splitSentences(line) {
86
82
  return line.split(/[.;]\s+(?=[A-Z`([-])/);
87
83
  }
88
- /** Backtick-quoted path-shaped tokens in a sentence (the same shape rule the
89
- * prohibition extractor applies), minus surrounding quotes. */
84
+ /** Backtick-quoted path-shaped tokens in a sentence, minus surrounding quotes.
85
+ * The same three-way test `looksLikePath` applies in prohibition-probe.ts:
86
+ * path characters only, and either a separator, an extension, or a leading
87
+ * dot. */
90
88
  function pathTokensIn(sentence) {
91
89
  const out = [];
92
90
  for (const m of sentence.matchAll(/`([^`]+)`/g)) {
@@ -103,11 +101,11 @@ function pathTokensIn(sentence) {
103
101
  * frozen path, appending each new pair to `out`.
104
102
  *
105
103
  * `anchorSpec` (research scans only): a research statement counts ONLY when,
106
- * besides the frozen path, it names at least one other path that still
107
- * appears in the SPEC. That anchor is what makes resolution (b) terminal:
108
- * when the rewrite DROPS the file creation, the created file vanishes from
109
- * the spec, the research statement about it loses its anchor, and the
110
- * detector goes quiet instead of re-firing forever on stale research. */
104
+ * besides the frozen path, it names at least one other path that still appears
105
+ * in the SPEC. That anchor is what makes resolution (b) terminal — measured,
106
+ * the same research sentence fires while the spec still creates the file and
107
+ * goes quiet once the spec no longer mentions it, instead of re-firing forever
108
+ * on stale research. */
111
109
  function scanForStatements(text, blanket, source, out, seen, anchorSpec) {
112
110
  for (const raw of text.split('\n')) {
113
111
  for (const fragment of splitSentences(raw)) {
@@ -146,10 +144,9 @@ function scanForStatements(text, blanket, source, out, seen, anchorSpec) {
146
144
  * SPEC alone; research contributes only the statement side (live compose
147
145
  * sometimes drops the research's "must also be included" nuance from the spec
148
146
  * text while shipping the freeze and the file creation - the contradiction is
149
- * then visible only across the compose boundary). Deterministic, pure text -
150
- * the same extraction the live A/B grounds its measurements in, so the model
151
- * cannot self-report its way past it. Empty on a null spec or one that froze
152
- * nothing.
147
+ * then visible only across the compose boundary). Deterministic and pure text,
148
+ * so the model cannot self-report its way past it. Empty on a null spec or one
149
+ * that froze nothing.
153
150
  */
154
151
  export function findFrozenPathConflicts(spec, research) {
155
152
  if (!spec)
@@ -171,11 +168,11 @@ export function findFrozenPathConflicts(spec, research) {
171
168
  return out;
172
169
  }
173
170
  /**
174
- * The forced critique-rewrite defect text (skip-escape pattern: MANDATORY,
175
- * self-contained, names the exact resolution options). The two permitted
176
- * resolutions come straight from the incident analysis: scoped ownership or
177
- * dropping the creation prose acknowledging the gap is called out as a
178
- * non-resolution because that is precisely what the live model shipped.
171
+ * The forced critique-rewrite defect text: MANDATORY, self-contained, and it
172
+ * names the exact resolution options. Only two are permitted — scoped ownership
173
+ * or dropping the creation — and prose acknowledging the gap is called out as a
174
+ * non-resolution, because that is the shape a model reaches for first and it
175
+ * leaves the contradiction intact.
179
176
  */
180
177
  export function frozenConflictProbeText(conflicts) {
181
178
  const items = conflicts.map(c => `- the spec FREEZES \`${c.path}\` ("${c.constraint.slice(0, 160)}") yet `
@@ -5,19 +5,24 @@ export type FrozenGit = (args: string[]) => Promise<{
5
5
  }>;
6
6
  /**
7
7
  * The concrete paths the spec forbids modifying, normalized and de-duplicated —
8
- * the SAME extraction the verify prohibition-probe and the accept-debt ledger
9
- * consume, so "frozen" means one thing across the gates. Empty when the spec is
10
- * null or names no path-like token on a modification-ban line: the guard is then a
11
- * no-op by construction.
8
+ * `extractProhibitions`, the same extraction the verify prohibition probe
9
+ * (gate-deps.ts) and the compose-time conflict detector (frozen-conflict.ts)
10
+ * consume, so "frozen" means one thing across the gates.
11
+ *
12
+ * Normalisation strips a leading `./` and a trailing `/`: measured, a spec
13
+ * banning `` `./src/a.ts` `` and `` `src/b/` `` yields `src/a.ts` and `src/b`.
14
+ * A null spec, or one with no modification-ban line naming a path, yields an
15
+ * empty list and the guard is a no-op by construction.
12
16
  */
13
17
  export declare function frozenPathsFromSpec(spec: string | null | undefined): string[];
14
18
  /**
15
- * Does this prose/tool-output text NAME the given path? Word-bounded on both
16
- * sides so `tsconfig.json` matches `` `tsconfig.json` ``, `(tsconfig.json:18)`
17
- * and a bare mention, but never `foo.tsconfig.json`, `config/tsconfig.json`
18
- * (a different file) or `tsconfig.json5`/`tsconfig.json.bak`. Shared by the
19
- * compose-time unsatisfiable-pair detector (frozen-conflict.ts) and lint-fix's
20
- * non-convergence trace, so "the text names a frozen path" means one thing.
19
+ * Does this prose or tool-output text NAME the given path? Word-bounded on both
20
+ * sides. Measured against `tsconfig.json`: `` `tsconfig.json` ``,
21
+ * `(tsconfig.json:18)` and a bare mention all match; `foo.tsconfig.json`,
22
+ * `config/tsconfig.json` (a different file), `tsconfig.json5` and
23
+ * `tsconfig.json.bak` all do not. Shared by the compose-time unsatisfiable-pair
24
+ * detector (frozen-conflict.ts) and lint-fix's non-convergence trace, so "the
25
+ * text names a frozen path" means one thing.
21
26
  */
22
27
  export declare function pathNamedIn(text: string, path: string): boolean;
23
28
  /**
@@ -26,6 +31,10 @@ export declare function pathNamedIn(text: string, path: string): boolean;
26
31
  * anything must be reverted at all. A rename line (`R old -> new`) yields the NEW
27
32
  * path — the side that carries the child's write. Deterministic and pure so the
28
33
  * parsing is unit-tested without a real repo.
34
+ *
35
+ * Every shape below was taken from real `git status --porcelain` output, not
36
+ * assumed: ` M path`, ` D path`, `?? path`, `R old -> new`, and a path
37
+ * containing a space, which git emits wrapped in double quotes.
29
38
  */
30
39
  export declare function parseChangedFrozenFiles(porcelain: string): string[];
31
40
  /**
@@ -36,10 +45,22 @@ export declare function parseChangedFrozenFiles(porcelain: string): string[];
36
45
  * Runs AFTER the task's own work is committed (HEAD), so "restore to HEAD" keeps
37
46
  * the verified task's version of the frozen file and discards ONLY the gate
38
47
  * child's edit on top of it — the task's own frozen-path edits, if any, are a
39
- * separate concern the verify prohibition-probe surfaces. `git checkout HEAD`
40
- * covers modified/deleted tracked files under each pathspec; `git clean` removes
41
- * any untracked file the pass created under a frozen directory. Both are scoped to
42
- * the frozen pathspec, so nothing else in the tree is touched.
48
+ * separate concern the verify prohibition probe surfaces. `git checkout -f HEAD`
49
+ * covers modified and deleted tracked files under each pathspec; `git clean -fdq`
50
+ * removes untracked files the pass created under a frozen directory. Both are
51
+ * scoped to the frozen pathspec.
52
+ *
53
+ * Run against a real repo with one frozen directory and one free one: a modified
54
+ * file came back to its HEAD content, a deleted file came back, and two untracked
55
+ * creations (one with a space in its name) were removed, while an edit to a file
56
+ * OUTSIDE the pathspec survived untouched.
57
+ *
58
+ * ONE CASE IS NOT FULLY UNDONE: a rename already STAGED in the index. `git mv`
59
+ * inside the frozen directory leaves `frozen/new.txt` present and staged after
60
+ * both commands, because checkout cannot restore a path HEAD does not contain and
61
+ * clean skips tracked files — while this function still reports it as reverted.
62
+ * An ordinary edit-tool rename is a delete plus an untracked create, and that IS
63
+ * fully undone; staging requires git, which the enforce child does not have.
43
64
  *
44
65
  * Best-effort: an empty frozen list, a non-git tree, or any git error yields an
45
66
  * empty result — the guard must never break the gate on a project it cannot reason
@@ -1,41 +1,41 @@
1
1
  /**
2
2
  * frozen-path-guard — deterministic write-deny on spec-frozen paths for the
3
- * write-capable gate children (the enforce EDIT pass especially).
3
+ * write-capable gate children, the enforce EDIT pass especially.
4
4
  *
5
- * The failure class (carried from mx5 run 6, deferred there as the last unshipped
6
- * piece of the frozen-contract work): a spec's CONSTRAINTS pin a path as
7
- * off-limits ("**Do NOT modify** `src/server/index.ts`"), and a later
8
- * write-enabled GATE pass the enforce child runs `read,edit` edits that path
9
- * anyway. Its edits are judged only locally (a guideline-compliance verdict), then
10
- * committed as an "ENFORCE GUIDELINES" snapshot on top of the verified task. The
11
- * frozen contract is silently mutated by the very pass meant to police the work.
5
+ * The failure class: a spec's CONSTRAINTS pin a path as off-limits ("**Do NOT
6
+ * modify** `src/server/index.ts`"), and a later write-enabled GATE pass edits it
7
+ * anyway. The enforce child runs with `ENFORCE_TOOLS = 'read,edit'`, its edits
8
+ * are judged only for guideline compliance, and they are then committed as an
9
+ * `ENFORCE GUIDELINES` snapshot on top of the verified task so the frozen
10
+ * contract gets mutated by the very pass meant to police the work.
12
11
  *
13
- * Prompt framing is A/B-PROVEN insufficient for this class (the "FROZEN CONTRACT,
14
- * MUST NOT edit" instruction held 0/5 unguarded, ~1/5 with framing the weak
15
- * local model ignores an explicit capitalized MUST-NOT most of the time). A
16
- * chmod-style physical deny holds 5/5 but makes the model THRASH on the bare
17
- * EACCES (1000+ futile re-attempts observed) unacceptable on the enforce child,
18
- * which runs UNGUARDED (no wall-clock timeout). pi itself has no path-level edit
19
- * interception (only tool-NAME allow/deny), so the achievable, thrash-free,
20
- * stack-agnostic realization of a tool-layer deny is this: let the pass edit
21
- * freely, then deterministically UNDO any frozen-path change it made before those
22
- * edits can be committed. The violating write never lands in a commit, regardless
23
- * of what the model intended — no model in the loop, same discipline as
24
- * git-state-guard.
12
+ * A tool-layer deny is not available. pi 0.84.4 offers `--tools` and
13
+ * `--exclude-tools`, both allow/deny by tool NAME; no flag scopes an edit to a
14
+ * path. A chmod-style physical deny would leave the child thrashing on a bare
15
+ * EACCES, and the enforce child has NO WALL CLOCK to stop it — the `gate` row of
16
+ * WORKER_PROFILES sets `worker-timeout` to `{timeoutMs: 0}`, leaving only a
17
+ * per-command watchdog and a stream watchdog.
25
18
  *
26
- * Everything degrades to a no-op when the spec froze nothing (a single-`/task`
27
- * run, or a spec with no `Do NOT modify` line naming a path → no frozen paths →
28
- * nothing snapshotted, nothing reverted) and on any git error. Pure git shape,
29
- * zero stack assumptions — a frozen path exists for a CLI, a library, a script
30
- * collection exactly as for a web app.
19
+ * So the achievable deny is after the fact: let the pass edit freely, then
20
+ * deterministically UNDO any frozen-path change before those edits can be
21
+ * committed. No model is in the loop, the same discipline as git-state-guard.
22
+ *
23
+ * Everything degrades to a no-op when the spec froze nothing — a single-`/task`
24
+ * run, or a spec with no `Do NOT modify` line naming a path — and on any git
25
+ * error. Pure git shape, zero stack assumptions: a frozen path exists for a CLI,
26
+ * a library or a script collection exactly as for a web app.
31
27
  */
32
28
  import { extractProhibitions } from './prohibition-probe.js';
33
29
  /**
34
30
  * The concrete paths the spec forbids modifying, normalized and de-duplicated —
35
- * the SAME extraction the verify prohibition-probe and the accept-debt ledger
36
- * consume, so "frozen" means one thing across the gates. Empty when the spec is
37
- * null or names no path-like token on a modification-ban line: the guard is then a
38
- * no-op by construction.
31
+ * `extractProhibitions`, the same extraction the verify prohibition probe
32
+ * (gate-deps.ts) and the compose-time conflict detector (frozen-conflict.ts)
33
+ * consume, so "frozen" means one thing across the gates.
34
+ *
35
+ * Normalisation strips a leading `./` and a trailing `/`: measured, a spec
36
+ * banning `` `./src/a.ts` `` and `` `src/b/` `` yields `src/a.ts` and `src/b`.
37
+ * A null spec, or one with no modification-ban line naming a path, yields an
38
+ * empty list and the guard is a no-op by construction.
39
39
  */
40
40
  export function frozenPathsFromSpec(spec) {
41
41
  if (!spec)
@@ -50,12 +50,13 @@ export function frozenPathsFromSpec(spec) {
50
50
  }
51
51
  const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
52
52
  /**
53
- * Does this prose/tool-output text NAME the given path? Word-bounded on both
54
- * sides so `tsconfig.json` matches `` `tsconfig.json` ``, `(tsconfig.json:18)`
55
- * and a bare mention, but never `foo.tsconfig.json`, `config/tsconfig.json`
56
- * (a different file) or `tsconfig.json5`/`tsconfig.json.bak`. Shared by the
57
- * compose-time unsatisfiable-pair detector (frozen-conflict.ts) and lint-fix's
58
- * non-convergence trace, so "the text names a frozen path" means one thing.
53
+ * Does this prose or tool-output text NAME the given path? Word-bounded on both
54
+ * sides. Measured against `tsconfig.json`: `` `tsconfig.json` ``,
55
+ * `(tsconfig.json:18)` and a bare mention all match; `foo.tsconfig.json`,
56
+ * `config/tsconfig.json` (a different file), `tsconfig.json5` and
57
+ * `tsconfig.json.bak` all do not. Shared by the compose-time unsatisfiable-pair
58
+ * detector (frozen-conflict.ts) and lint-fix's non-convergence trace, so "the
59
+ * text names a frozen path" means one thing.
59
60
  */
60
61
  export function pathNamedIn(text, path) {
61
62
  const p = path.replace(/^\.\//, '').replace(/\/+$/, '');
@@ -69,6 +70,10 @@ export function pathNamedIn(text, path) {
69
70
  * anything must be reverted at all. A rename line (`R old -> new`) yields the NEW
70
71
  * path — the side that carries the child's write. Deterministic and pure so the
71
72
  * parsing is unit-tested without a real repo.
73
+ *
74
+ * Every shape below was taken from real `git status --porcelain` output, not
75
+ * assumed: ` M path`, ` D path`, `?? path`, `R old -> new`, and a path
76
+ * containing a space, which git emits wrapped in double quotes.
72
77
  */
73
78
  export function parseChangedFrozenFiles(porcelain) {
74
79
  const out = [];
@@ -104,10 +109,22 @@ export function parseChangedFrozenFiles(porcelain) {
104
109
  * Runs AFTER the task's own work is committed (HEAD), so "restore to HEAD" keeps
105
110
  * the verified task's version of the frozen file and discards ONLY the gate
106
111
  * child's edit on top of it — the task's own frozen-path edits, if any, are a
107
- * separate concern the verify prohibition-probe surfaces. `git checkout HEAD`
108
- * covers modified/deleted tracked files under each pathspec; `git clean` removes
109
- * any untracked file the pass created under a frozen directory. Both are scoped to
110
- * the frozen pathspec, so nothing else in the tree is touched.
112
+ * separate concern the verify prohibition probe surfaces. `git checkout -f HEAD`
113
+ * covers modified and deleted tracked files under each pathspec; `git clean -fdq`
114
+ * removes untracked files the pass created under a frozen directory. Both are
115
+ * scoped to the frozen pathspec.
116
+ *
117
+ * Run against a real repo with one frozen directory and one free one: a modified
118
+ * file came back to its HEAD content, a deleted file came back, and two untracked
119
+ * creations (one with a space in its name) were removed, while an edit to a file
120
+ * OUTSIDE the pathspec survived untouched.
121
+ *
122
+ * ONE CASE IS NOT FULLY UNDONE: a rename already STAGED in the index. `git mv`
123
+ * inside the frozen directory leaves `frozen/new.txt` present and staged after
124
+ * both commands, because checkout cannot restore a path HEAD does not contain and
125
+ * clean skips tracked files — while this function still reports it as reverted.
126
+ * An ordinary edit-tool rename is a delete plus an untracked create, and that IS
127
+ * fully undone; staging requires git, which the enforce child does not have.
111
128
  *
112
129
  * Best-effort: an empty frozen list, a non-git tree, or any git error yields an
113
130
  * empty result — the guard must never break the gate on a project it cannot reason