dorfl 0.14.3 → 0.15.0

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 (418) hide show
  1. package/dist/advance-ci-template.d.ts +17 -16
  2. package/dist/advance-ci-template.d.ts.map +1 -1
  3. package/dist/advance-ci-template.js +42 -53
  4. package/dist/advance-ci-template.js.map +1 -1
  5. package/dist/advance-drivers.d.ts +8 -0
  6. package/dist/advance-drivers.d.ts.map +1 -1
  7. package/dist/advance-drivers.js +28 -4
  8. package/dist/advance-drivers.js.map +1 -1
  9. package/dist/advance-isolated.d.ts.map +1 -1
  10. package/dist/advance-isolated.js +3 -2
  11. package/dist/advance-isolated.js.map +1 -1
  12. package/dist/advance-lifecycle-template.d.ts +41 -40
  13. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  14. package/dist/advance-lifecycle-template.js +305 -486
  15. package/dist/advance-lifecycle-template.js.map +1 -1
  16. package/dist/advance-loop-driver.d.ts.map +1 -1
  17. package/dist/advance-loop-driver.js +3 -2
  18. package/dist/advance-loop-driver.js.map +1 -1
  19. package/dist/advance-treeless-publish.d.ts +31 -0
  20. package/dist/advance-treeless-publish.d.ts.map +1 -1
  21. package/dist/advance-treeless-publish.js +40 -0
  22. package/dist/advance-treeless-publish.js.map +1 -1
  23. package/dist/advance.d.ts +63 -3
  24. package/dist/advance.d.ts.map +1 -1
  25. package/dist/advance.js +80 -12
  26. package/dist/advance.js.map +1 -1
  27. package/dist/advancing-lock.d.ts +9 -0
  28. package/dist/advancing-lock.d.ts.map +1 -1
  29. package/dist/advancing-lock.js +21 -1
  30. package/dist/advancing-lock.js.map +1 -1
  31. package/dist/agent-env.d.ts +61 -0
  32. package/dist/agent-env.d.ts.map +1 -0
  33. package/dist/agent-env.js +144 -0
  34. package/dist/agent-env.js.map +1 -0
  35. package/dist/agent-stop.d.ts +35 -0
  36. package/dist/agent-stop.d.ts.map +1 -1
  37. package/dist/agent-stop.js +62 -0
  38. package/dist/agent-stop.js.map +1 -1
  39. package/dist/apply-merge-action.d.ts +76 -34
  40. package/dist/apply-merge-action.d.ts.map +1 -1
  41. package/dist/apply-merge-action.js +339 -129
  42. package/dist/apply-merge-action.js.map +1 -1
  43. package/dist/apply-stuck-action.js +2 -2
  44. package/dist/apply-stuck-action.js.map +1 -1
  45. package/dist/ci-agent-result.d.ts +151 -0
  46. package/dist/ci-agent-result.d.ts.map +1 -0
  47. package/dist/ci-agent-result.js +314 -0
  48. package/dist/ci-agent-result.js.map +1 -0
  49. package/dist/ci-handoff-apply.d.ts +162 -0
  50. package/dist/ci-handoff-apply.d.ts.map +1 -0
  51. package/dist/ci-handoff-apply.js +497 -0
  52. package/dist/ci-handoff-apply.js.map +1 -0
  53. package/dist/ci-handoff-format.d.ts +369 -0
  54. package/dist/ci-handoff-format.d.ts.map +1 -0
  55. package/dist/ci-handoff-format.js +646 -0
  56. package/dist/ci-handoff-format.js.map +1 -0
  57. package/dist/ci-handoff-lfs.d.ts +146 -0
  58. package/dist/ci-handoff-lfs.d.ts.map +1 -0
  59. package/dist/ci-handoff-lfs.js +397 -0
  60. package/dist/ci-handoff-lfs.js.map +1 -0
  61. package/dist/ci-handoff.d.ts +99 -0
  62. package/dist/ci-handoff.d.ts.map +1 -0
  63. package/dist/ci-handoff.js +277 -0
  64. package/dist/ci-handoff.js.map +1 -0
  65. package/dist/ci-lock-outputs.d.ts +93 -0
  66. package/dist/ci-lock-outputs.d.ts.map +1 -0
  67. package/dist/ci-lock-outputs.js +223 -0
  68. package/dist/ci-lock-outputs.js.map +1 -0
  69. package/dist/ci-phase-build.d.ts +178 -0
  70. package/dist/ci-phase-build.d.ts.map +1 -0
  71. package/dist/ci-phase-build.js +989 -0
  72. package/dist/ci-phase-build.js.map +1 -0
  73. package/dist/ci-phase-driver.d.ts +247 -0
  74. package/dist/ci-phase-driver.d.ts.map +1 -0
  75. package/dist/ci-phase-driver.js +370 -0
  76. package/dist/ci-phase-driver.js.map +1 -0
  77. package/dist/ci-phase-intake.d.ts +172 -0
  78. package/dist/ci-phase-intake.d.ts.map +1 -0
  79. package/dist/ci-phase-intake.js +555 -0
  80. package/dist/ci-phase-intake.js.map +1 -0
  81. package/dist/ci-phase-tasking.d.ts +205 -0
  82. package/dist/ci-phase-tasking.d.ts.map +1 -0
  83. package/dist/ci-phase-tasking.js +906 -0
  84. package/dist/ci-phase-tasking.js.map +1 -0
  85. package/dist/ci-phase-treeless.d.ts +224 -0
  86. package/dist/ci-phase-treeless.d.ts.map +1 -0
  87. package/dist/ci-phase-treeless.js +1482 -0
  88. package/dist/ci-phase-treeless.js.map +1 -0
  89. package/dist/ci-read-token.d.ts +47 -0
  90. package/dist/ci-read-token.d.ts.map +1 -0
  91. package/dist/ci-read-token.js +80 -0
  92. package/dist/ci-read-token.js.map +1 -0
  93. package/dist/cli.d.ts.map +1 -1
  94. package/dist/cli.js +447 -22
  95. package/dist/cli.js.map +1 -1
  96. package/dist/close-job-template.d.ts +10 -1
  97. package/dist/close-job-template.d.ts.map +1 -1
  98. package/dist/close-job-template.js +44 -13
  99. package/dist/close-job-template.js.map +1 -1
  100. package/dist/close-job.d.ts +20 -5
  101. package/dist/close-job.d.ts.map +1 -1
  102. package/dist/close-job.js +65 -12
  103. package/dist/close-job.js.map +1 -1
  104. package/dist/complete.d.ts.map +1 -1
  105. package/dist/complete.js +20 -1
  106. package/dist/complete.js.map +1 -1
  107. package/dist/config.d.ts +41 -44
  108. package/dist/config.d.ts.map +1 -1
  109. package/dist/config.js +2 -4
  110. package/dist/config.js.map +1 -1
  111. package/dist/continue-branch.d.ts +63 -15
  112. package/dist/continue-branch.d.ts.map +1 -1
  113. package/dist/continue-branch.js +75 -8
  114. package/dist/continue-branch.js.map +1 -1
  115. package/dist/crashed-run-save.d.ts +73 -0
  116. package/dist/crashed-run-save.d.ts.map +1 -0
  117. package/dist/crashed-run-save.js +86 -0
  118. package/dist/crashed-run-save.js.map +1 -0
  119. package/dist/cwd-section.d.ts.map +1 -1
  120. package/dist/cwd-section.js +10 -2
  121. package/dist/cwd-section.js.map +1 -1
  122. package/dist/decision-engine.d.ts +10 -0
  123. package/dist/decision-engine.d.ts.map +1 -1
  124. package/dist/decision-engine.js.map +1 -1
  125. package/dist/do-config.d.ts +2 -24
  126. package/dist/do-config.d.ts.map +1 -1
  127. package/dist/do-config.js +1 -33
  128. package/dist/do-config.js.map +1 -1
  129. package/dist/do.d.ts +149 -1
  130. package/dist/do.d.ts.map +1 -1
  131. package/dist/do.js +115 -25
  132. package/dist/do.js.map +1 -1
  133. package/dist/dorfl-item-template.d.ts +98 -0
  134. package/dist/dorfl-item-template.d.ts.map +1 -0
  135. package/dist/dorfl-item-template.js +504 -0
  136. package/dist/dorfl-item-template.js.map +1 -0
  137. package/dist/env-config.d.ts.map +1 -1
  138. package/dist/env-config.js +7 -5
  139. package/dist/env-config.js.map +1 -1
  140. package/dist/frontmatter.d.ts +52 -0
  141. package/dist/frontmatter.d.ts.map +1 -1
  142. package/dist/frontmatter.js +136 -2
  143. package/dist/frontmatter.js.map +1 -1
  144. package/dist/gc.d.ts +13 -0
  145. package/dist/gc.d.ts.map +1 -1
  146. package/dist/gc.js +16 -2
  147. package/dist/gc.js.map +1 -1
  148. package/dist/git.d.ts +18 -1
  149. package/dist/git.d.ts.map +1 -1
  150. package/dist/git.js +70 -5
  151. package/dist/git.js.map +1 -1
  152. package/dist/harness.d.ts +44 -5
  153. package/dist/harness.d.ts.map +1 -1
  154. package/dist/harness.js +24 -1
  155. package/dist/harness.js.map +1 -1
  156. package/dist/index.d.ts +11 -4
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +7 -3
  159. package/dist/index.js.map +1 -1
  160. package/dist/install-ci-action-pins.d.ts +25 -0
  161. package/dist/install-ci-action-pins.d.ts.map +1 -1
  162. package/dist/install-ci-action-pins.js +25 -0
  163. package/dist/install-ci-action-pins.js.map +1 -1
  164. package/dist/install-ci-actions-pr-creation.d.ts +70 -0
  165. package/dist/install-ci-actions-pr-creation.d.ts.map +1 -0
  166. package/dist/install-ci-actions-pr-creation.js +106 -0
  167. package/dist/install-ci-actions-pr-creation.js.map +1 -0
  168. package/dist/install-ci-capabilities/dorfl-item.d.ts +12 -0
  169. package/dist/install-ci-capabilities/dorfl-item.d.ts.map +1 -0
  170. package/dist/install-ci-capabilities/dorfl-item.js +26 -0
  171. package/dist/install-ci-capabilities/dorfl-item.js.map +1 -0
  172. package/dist/install-ci-core.d.ts +148 -37
  173. package/dist/install-ci-core.d.ts.map +1 -1
  174. package/dist/install-ci-core.js +278 -190
  175. package/dist/install-ci-core.js.map +1 -1
  176. package/dist/install-ci-github.d.ts +47 -2
  177. package/dist/install-ci-github.d.ts.map +1 -1
  178. package/dist/install-ci-github.js +113 -1
  179. package/dist/install-ci-github.js.map +1 -1
  180. package/dist/install-ci.d.ts +25 -6
  181. package/dist/install-ci.d.ts.map +1 -1
  182. package/dist/install-ci.js +72 -36
  183. package/dist/install-ci.js.map +1 -1
  184. package/dist/intake-marker.d.ts +6 -1
  185. package/dist/intake-marker.d.ts.map +1 -1
  186. package/dist/intake-marker.js.map +1 -1
  187. package/dist/intake-triage.d.ts.map +1 -1
  188. package/dist/intake-triage.js +6 -2
  189. package/dist/intake-triage.js.map +1 -1
  190. package/dist/intake-trigger-template.d.ts +31 -25
  191. package/dist/intake-trigger-template.d.ts.map +1 -1
  192. package/dist/intake-trigger-template.js +107 -293
  193. package/dist/intake-trigger-template.js.map +1 -1
  194. package/dist/intake.d.ts +186 -4
  195. package/dist/intake.d.ts.map +1 -1
  196. package/dist/intake.js +533 -416
  197. package/dist/intake.js.map +1 -1
  198. package/dist/integration-core.d.ts +154 -1
  199. package/dist/integration-core.d.ts.map +1 -1
  200. package/dist/integration-core.js +759 -486
  201. package/dist/integration-core.js.map +1 -1
  202. package/dist/integrator.d.ts +12 -0
  203. package/dist/integrator.d.ts.map +1 -1
  204. package/dist/integrator.js +21 -0
  205. package/dist/integrator.js.map +1 -1
  206. package/dist/isolation.d.ts +12 -2
  207. package/dist/isolation.d.ts.map +1 -1
  208. package/dist/isolation.js +19 -8
  209. package/dist/isolation.js.map +1 -1
  210. package/dist/item-lock.d.ts +93 -11
  211. package/dist/item-lock.d.ts.map +1 -1
  212. package/dist/item-lock.js +233 -65
  213. package/dist/item-lock.js.map +1 -1
  214. package/dist/land-question-residue.d.ts +58 -0
  215. package/dist/land-question-residue.d.ts.map +1 -0
  216. package/dist/land-question-residue.js +95 -0
  217. package/dist/land-question-residue.js.map +1 -0
  218. package/dist/lifecycle-gather.d.ts +16 -0
  219. package/dist/lifecycle-gather.d.ts.map +1 -1
  220. package/dist/lifecycle-gather.js +54 -6
  221. package/dist/lifecycle-gather.js.map +1 -1
  222. package/dist/merge-answer.d.ts +41 -0
  223. package/dist/merge-answer.d.ts.map +1 -0
  224. package/dist/merge-answer.js +60 -0
  225. package/dist/merge-answer.js.map +1 -0
  226. package/dist/merge-question-surfacer.d.ts +105 -30
  227. package/dist/merge-question-surfacer.d.ts.map +1 -1
  228. package/dist/merge-question-surfacer.js +202 -91
  229. package/dist/merge-question-surfacer.js.map +1 -1
  230. package/dist/merge-question-tick.d.ts +64 -0
  231. package/dist/merge-question-tick.d.ts.map +1 -0
  232. package/dist/merge-question-tick.js +96 -0
  233. package/dist/merge-question-tick.js.map +1 -0
  234. package/dist/mint-adr.d.ts.map +1 -1
  235. package/dist/mint-adr.js +20 -2
  236. package/dist/mint-adr.js.map +1 -1
  237. package/dist/needs-attention.d.ts +72 -2
  238. package/dist/needs-attention.d.ts.map +1 -1
  239. package/dist/needs-attention.js +275 -38
  240. package/dist/needs-attention.js.map +1 -1
  241. package/dist/phase-recorder.d.ts +168 -0
  242. package/dist/phase-recorder.d.ts.map +1 -0
  243. package/dist/phase-recorder.js +218 -0
  244. package/dist/phase-recorder.js.map +1 -0
  245. package/dist/phase.d.ts +94 -0
  246. package/dist/phase.d.ts.map +1 -0
  247. package/dist/phase.js +148 -0
  248. package/dist/phase.js.map +1 -0
  249. package/dist/pi-harness.d.ts.map +1 -1
  250. package/dist/pi-harness.js +19 -6
  251. package/dist/pi-harness.js.map +1 -1
  252. package/dist/prepare.d.ts.map +1 -1
  253. package/dist/prepare.js +4 -0
  254. package/dist/prepare.js.map +1 -1
  255. package/dist/prompt.d.ts +4 -0
  256. package/dist/prompt.d.ts.map +1 -1
  257. package/dist/prompt.js +48 -1
  258. package/dist/prompt.js.map +1 -1
  259. package/dist/protocol/CLAIM-PROTOCOL.md +3 -1
  260. package/dist/protocol/WORK-CONTRACT.md +1 -1
  261. package/dist/reap-branches.d.ts +48 -0
  262. package/dist/reap-branches.d.ts.map +1 -1
  263. package/dist/reap-branches.js +64 -0
  264. package/dist/reap-branches.js.map +1 -1
  265. package/dist/ref-write.d.ts +198 -0
  266. package/dist/ref-write.d.ts.map +1 -0
  267. package/dist/ref-write.js +79 -0
  268. package/dist/ref-write.js.map +1 -0
  269. package/dist/repo-config.js +8 -8
  270. package/dist/repo-config.js.map +1 -1
  271. package/dist/repo-mirror.d.ts +36 -0
  272. package/dist/repo-mirror.d.ts.map +1 -1
  273. package/dist/repo-mirror.js +91 -1
  274. package/dist/repo-mirror.js.map +1 -1
  275. package/dist/review-verdict.d.ts +1 -1
  276. package/dist/review-verdict.js +1 -1
  277. package/dist/run.d.ts +7 -1
  278. package/dist/run.d.ts.map +1 -1
  279. package/dist/run.js +28 -4
  280. package/dist/run.js.map +1 -1
  281. package/dist/scan.d.ts +19 -1
  282. package/dist/scan.d.ts.map +1 -1
  283. package/dist/scan.js +21 -8
  284. package/dist/scan.js.map +1 -1
  285. package/dist/sidecar.d.ts +14 -0
  286. package/dist/sidecar.d.ts.map +1 -1
  287. package/dist/sidecar.js +21 -0
  288. package/dist/sidecar.js.map +1 -1
  289. package/dist/single-job-warning.d.ts +63 -0
  290. package/dist/single-job-warning.d.ts.map +1 -0
  291. package/dist/single-job-warning.js +110 -0
  292. package/dist/single-job-warning.js.map +1 -0
  293. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +3 -1
  294. package/dist/skills/setup/protocol/WORK-CONTRACT.md +1 -1
  295. package/dist/spec-complete.d.ts +13 -6
  296. package/dist/spec-complete.d.ts.map +1 -1
  297. package/dist/spec-complete.js +23 -11
  298. package/dist/spec-complete.js.map +1 -1
  299. package/dist/start.d.ts.map +1 -1
  300. package/dist/start.js +47 -14
  301. package/dist/start.js.map +1 -1
  302. package/dist/status.d.ts +12 -0
  303. package/dist/status.d.ts.map +1 -1
  304. package/dist/status.js +11 -3
  305. package/dist/status.js.map +1 -1
  306. package/dist/tasking.d.ts +246 -1
  307. package/dist/tasking.d.ts.map +1 -1
  308. package/dist/tasking.js +452 -358
  309. package/dist/tasking.js.map +1 -1
  310. package/dist/triage-persist.d.ts.map +1 -1
  311. package/dist/triage-persist.js +21 -2
  312. package/dist/triage-persist.js.map +1 -1
  313. package/dist/verify-workflow-template.d.ts.map +1 -1
  314. package/dist/verify-workflow-template.js +6 -3
  315. package/dist/verify-workflow-template.js.map +1 -1
  316. package/dist/verify.d.ts.map +1 -1
  317. package/dist/verify.js +4 -0
  318. package/dist/verify.js.map +1 -1
  319. package/dist/watch-session.d.ts +42 -13
  320. package/dist/watch-session.d.ts.map +1 -1
  321. package/dist/watch-session.js +52 -23
  322. package/dist/watch-session.js.map +1 -1
  323. package/dist/work-layout.d.ts +10 -4
  324. package/dist/work-layout.d.ts.map +1 -1
  325. package/dist/work-layout.js +11 -3
  326. package/dist/work-layout.js.map +1 -1
  327. package/dist/workspace.d.ts +65 -4
  328. package/dist/workspace.d.ts.map +1 -1
  329. package/dist/workspace.js +159 -11
  330. package/dist/workspace.js.map +1 -1
  331. package/package.json +1 -1
  332. package/src/advance-ci-template.ts +80 -81
  333. package/src/advance-drivers.ts +45 -6
  334. package/src/advance-isolated.ts +3 -5
  335. package/src/advance-lifecycle-template.ts +371 -535
  336. package/src/advance-loop-driver.ts +3 -5
  337. package/src/advance-treeless-publish.ts +61 -0
  338. package/src/advance.ts +145 -15
  339. package/src/advancing-lock.ts +36 -1
  340. package/src/agent-env.ts +174 -0
  341. package/src/agent-stop.ts +81 -0
  342. package/src/apply-merge-action.ts +401 -154
  343. package/src/apply-stuck-action.ts +2 -2
  344. package/src/ci-agent-result.ts +421 -0
  345. package/src/ci-handoff-apply.ts +737 -0
  346. package/src/ci-handoff-format.ts +926 -0
  347. package/src/ci-handoff-lfs.ts +519 -0
  348. package/src/ci-handoff.ts +392 -0
  349. package/src/ci-lock-outputs.ts +273 -0
  350. package/src/ci-phase-build.ts +1328 -0
  351. package/src/ci-phase-driver.ts +548 -0
  352. package/src/ci-phase-intake.ts +861 -0
  353. package/src/ci-phase-tasking.ts +1255 -0
  354. package/src/ci-phase-treeless.ts +2028 -0
  355. package/src/ci-read-token.ts +90 -0
  356. package/src/cli.ts +591 -29
  357. package/src/close-job-template.ts +50 -14
  358. package/src/close-job.ts +77 -12
  359. package/src/complete.ts +19 -1
  360. package/src/config.ts +43 -48
  361. package/src/continue-branch.ts +121 -16
  362. package/src/crashed-run-save.ts +162 -0
  363. package/src/cwd-section.ts +10 -0
  364. package/src/decision-engine.ts +10 -0
  365. package/src/do-config.ts +2 -57
  366. package/src/do.ts +170 -28
  367. package/src/dorfl-item-template.ts +556 -0
  368. package/src/env-config.ts +7 -5
  369. package/src/frontmatter.ts +155 -2
  370. package/src/gc.ts +27 -2
  371. package/src/git.ts +83 -5
  372. package/src/harness.ts +58 -6
  373. package/src/index.ts +50 -1
  374. package/src/install-ci-action-pins.ts +25 -0
  375. package/src/install-ci-actions-pr-creation.ts +186 -0
  376. package/src/install-ci-capabilities/dorfl-item.ts +33 -0
  377. package/src/install-ci-core.ts +352 -223
  378. package/src/install-ci-github.ts +149 -1
  379. package/src/install-ci.ts +83 -37
  380. package/src/intake-marker.ts +6 -1
  381. package/src/intake-triage.ts +7 -3
  382. package/src/intake-trigger-template.ts +126 -328
  383. package/src/intake.ts +725 -544
  384. package/src/integration-core.ts +975 -523
  385. package/src/integrator.ts +28 -0
  386. package/src/isolation.ts +29 -10
  387. package/src/item-lock.ts +308 -92
  388. package/src/land-question-residue.ts +122 -0
  389. package/src/lifecycle-gather.ts +82 -10
  390. package/src/merge-answer.ts +82 -0
  391. package/src/merge-question-surfacer.ts +320 -112
  392. package/src/merge-question-tick.ts +182 -0
  393. package/src/mint-adr.ts +28 -2
  394. package/src/needs-attention.ts +350 -42
  395. package/src/phase-recorder.ts +350 -0
  396. package/src/phase.ts +177 -0
  397. package/src/pi-harness.ts +25 -5
  398. package/src/prepare.ts +4 -0
  399. package/src/prompt.ts +68 -1
  400. package/src/reap-branches.ts +107 -0
  401. package/src/ref-write.ts +324 -0
  402. package/src/repo-config.ts +8 -8
  403. package/src/repo-mirror.ts +107 -1
  404. package/src/review-verdict.ts +1 -1
  405. package/src/run.ts +47 -6
  406. package/src/scan.ts +37 -7
  407. package/src/sidecar.ts +36 -0
  408. package/src/single-job-warning.ts +135 -0
  409. package/src/spec-complete.ts +36 -14
  410. package/src/start.ts +55 -16
  411. package/src/status.ts +29 -3
  412. package/src/tasking.ts +593 -378
  413. package/src/triage-persist.ts +24 -2
  414. package/src/verify-workflow-template.ts +6 -3
  415. package/src/verify.ts +4 -0
  416. package/src/watch-session.ts +86 -28
  417. package/src/work-layout.ts +11 -3
  418. package/src/workspace.ts +210 -14
@@ -7,9 +7,12 @@
7
7
  * `advance-lifecycle-template.ts` / `advance-ci-template.ts` (the package depends on
8
8
  * NO YAML lib, so the checks are presence/shape assertions over the raw text). It
9
9
  * ALSO carries the PURE intake-flags DERIVATION ({@link deriveIntakeFlags}) —
10
- * CI's merge-vs-propose POLICY, the load-bearing testable logic the workflow
11
- * encodes at runtime. The DOCUMENT mode is the resolved `intakeIntegration`
12
- * (operator/config); author-trust drives ONLY the `--origin-trust` stamp +
10
+ * CI's merge-vs-propose POLICY. Since the CI split (task
11
+ * `ci-split-generate-workflows`) the workflow no longer derives it in a shell
12
+ * step: the called `dorfl-item.yml` lock job derives the same rule from trusted
13
+ * inputs (`ci-phase-intake.ts`: the event's author association, `dorfl.json`
14
+ * at the base). The DOCUMENT mode is the resolved `intakeIntegration`
15
+ * (operator/config); author-trust drives ONLY the origin-trust stamp +
13
16
  * placement, never the mode.
14
17
  *
15
18
  * SCOPE FENCE (spec Out-of-Scope): the issue→artifact TRANSFORM engine is
@@ -22,8 +25,10 @@
22
25
  * RESOLVED design decisions in the task):
23
26
  *
24
27
  * - TRIGGERS: `issues` opened, `issue_comment` created, and a label
25
- * (`dorfl:intake`). It invokes `intake <N>` (EXPLICIT, four-outcome
26
- * dispatch) — never a bare slug.
28
+ * (`dorfl:intake`). Its one job calls `dorfl-item.yml` with
29
+ * `item: issue:<N>` (lock, agent, apply; decision 10 of ADR
30
+ * `ci-agent-job-holds-no-write-token`), which runs `intake <N>` (EXPLICIT,
31
+ * four-outcome dispatch) — never a bare slug.
27
32
  * - EVENT→`IntakeEventKind` MAPPING (Decision 2: no edit-detection): only a
28
33
  * CREATED `issue_comment` (and an opened issue / the label) drives
29
34
  * (re-)evaluation; an EDITED comment is NOT a trigger (the ID-based
@@ -58,9 +63,11 @@
58
63
  * overlapping ticks on the SAME issue; the `processing` lock / claim CAS is the
59
64
  * real cross-run serialiser.
60
65
  * - The running CI job NEVER edits `.github/workflows/**` (US #9): it requests NO
61
- * `workflows` permission and cannot rewrite its own triggers. It needs
66
+ * `workflows` permission and cannot rewrite its own triggers. It grants
62
67
  * `contents: write` + `pull-requests: write` (emit/propose the artifact) +
63
- * `issues: write` (post the clarifying/review comment back).
68
+ * `issues: write` (post the clarifying/review comment back) + `actions` and
69
+ * `checks: read` (the apply job tells a timed-out agent job from a
70
+ * cancelled one), which the called workflow's jobs narrow per job.
64
71
  *
65
72
  * The structural validator is the dependency-free counterpart of "the workflow
66
73
  * parses + carries the right discipline" the task's acceptance criteria require;
@@ -68,9 +75,11 @@
68
75
  */
69
76
 
70
77
  import {brand} from './brand.js';
71
- import {ACTION_PINS, pinnedUses} from './install-ci-action-pins.js';
72
78
  import type {ResolvedCIConfig} from './install-ci-core.js';
73
- import {providerSecretsWithBlock} from './install-ci-core.js';
79
+ import {
80
+ ITEM_WORKFLOW_CALLER_PERMISSIONS,
81
+ itemCallSecrets,
82
+ } from './dorfl-item-template.js';
74
83
 
75
84
  /** The capability id (the registry key + the emitted workflow file stem). */
76
85
  export const INTAKE_TRIGGER_CAPABILITY_ID = 'intake';
@@ -162,9 +171,10 @@ export interface IntakeIntegrationFlags {
162
171
  * DERIVE the per-outcome file-emit modes + the origin-trust stamp — CI's intake
163
172
  * POLICY (ADR `untrusted-origin-carries-via-stamp-not-forced-staging`; spec
164
173
  * `intake-integration-knob-and-specs-land-in-proposed-rename` US #1/#2). This is
165
- * the load-bearing pure logic the workflow encodes at runtime (it reads the
166
- * resolved `intakeIntegration ?? integration` via `dorfl config --json` for the
167
- * MODE, and `author_association` off the event payload for the STAMP):
174
+ * the load-bearing pure rule the intake lock job applies at runtime (it reads
175
+ * the resolved `intakeIntegration ?? integration` from `dorfl.json` at the base
176
+ * for the MODE, and `author_association` off the event payload for the STAMP;
177
+ * `ci-phase-intake.ts`):
168
178
  *
169
179
  * - **DOCUMENT mode (spec = task)** — the resolved `intakeIntegration` value, a
170
180
  * SINGLE mode applied to BOTH the spec and task document: `merge` ⇒
@@ -233,27 +243,22 @@ export function isAuthorTrusted(
233
243
 
234
244
  /**
235
245
  * Generate the intake-trigger workflow YAML. Deterministic: the same config
236
- * produces byte-identical output. The workflow is a FIXED shell (ADR §6: all
237
- * policy is env/config, so the artifact carries no config-derived policy beyond
238
- * the env-block scaffolding) — `config` is accepted for parity with the
239
- * `CapabilityEmitter` seam and future per-config wiring, but the intake-trigger
240
- * shape itself is config-independent.
246
+ * produces byte-identical output.
241
247
  *
242
- * The per-outcome FLAGS are DERIVED AT RUNTIME by a `bash` step that mirrors
243
- * {@link deriveIntakeFlags}: it reads the resolved `intakeIntegration ??
244
- * integration` via `dorfl config --json` to set the (config-derived) DOCUMENT
245
- * mode `--merge-spec`/`--propose-spec` + `--merge-task`/`--propose-task` (a SINGLE
246
- * mode applied to both), and the event's `author_association` to set ONLY the
247
- * `--origin-trust` STAMP (which carries the placement + build-PR consequence). The
248
- * autonomy gates (`autoBuild`/`autoTask`) are NOT read for the document mode (they
249
- * mean only "may an agent act autonomously"). The same rule that
250
- * {@link deriveIntakeFlags} unit-tests is what the workflow executes — they cannot
251
- * desync because the test asserts the SHELL derivation matches the function.
248
+ * THE SPLIT (spec `ci-agent-job-without-write-token`, decision 10 of ADR
249
+ * `ci-agent-job-holds-no-write-token`; task `ci-split-generate-workflows`): the
250
+ * one job CALLS the per-item workflow `dorfl-item.yml` with
251
+ * `item: issue:<N>`, under this workflow's per-issue concurrency group. Its lock
252
+ * job (write token, no agent) takes the `processing` label and DERIVES the
253
+ * intake policy itself: the origin-trust stamp from this run's event
254
+ * (`comment.author_association`, else `issue.author_association`; a called
255
+ * workflow sees its caller's event) and the document mode from
256
+ * `intakeIntegration ?? integration` in `dorfl.json` at the base, the same rule
257
+ * {@link deriveIntakeFlags} states. The decision agent runs in the agent job
258
+ * (read-only token), and the apply job (write token, no agent) posts the
259
+ * comment or integrates the document.
252
260
  */
253
261
  export function generateIntakeWorkflow(config: ResolvedCIConfig): string {
254
- // `intake <N>` runs the prompt→verdict decision (the agent), so it needs the
255
- // provider secret(s) forwarded to `$GITHUB_ENV` by the setup action.
256
- const setupWith = providerSecretsWithBlock(config);
257
262
  return `\
258
263
  # dorfl — the ISSUE INTAKE trigger in CI (capability D: consider incoming
259
264
  # issues → task/spec, PLUS insertion point E: surface the review verdict into the
@@ -268,6 +273,14 @@ export function generateIntakeWorkflow(config: ResolvedCIConfig): string {
268
273
  # findings as questions back into THIS issue thread via the issue-comment seam (insertion
269
274
  # point E) — REUSED, not a new review mechanism.
270
275
  #
276
+ # THE SPLIT (ADR ci-agent-job-holds-no-write-token): ANY GitHub user can write
277
+ # the issue text the decision agent reads, so the agent never shares a job with
278
+ # a write token. The one job below calls dorfl-item.yml with
279
+ # \`item: issue:<N>\`: a LOCK job (write token, no agent: takes the \`processing\`
280
+ # label), an AGENT job (read-only token, no persisted credential: runs the
281
+ # decision) and an APPLY job (write token, no agent: validates the agent's
282
+ # handoff as hostile, then comments or integrates the document).
283
+ #
271
284
  # TRIGGERS (capability D): an OPENED issue, a CREATED issue comment, and the
272
285
  # \`${INTAKE_TRIGGER_LABEL}\` label. A CREATED comment is the (re-)evaluation
273
286
  # trigger; an EDITED comment is deliberately NOT a trigger (the ID-based
@@ -277,31 +290,29 @@ export function generateIntakeWorkflow(config: ResolvedCIConfig): string {
277
290
  # re-evaluation (a fresh id the watermark catches). There is NO edit-detection /
278
291
  # \`updated_at\` / body-hash tracking.
279
292
  #
280
- # THE DOCUMENT MODE is \`intakeIntegration\`; AUTHOR-TRUST → PLACEMENT + the STAMP:
281
- # the intake DOCUMENT merge-vs-propose mode is the resolved \`intakeIntegration ??
282
- # integration\` (an operator/config choice, a SINGLE value applied to BOTH the task
283
- # and spec document), DECOUPLED from the autonomy gates (spec
284
- # intake-integration-knob-and-specs-land-in-proposed-rename; ADR
285
- # untrusted-origin-carries-via-stamp-not-forced-staging). \`autoBuild\`/\`autoTask\`
286
- # no longer decide the document mode (they mean only "may an agent act
287
- # autonomously"). Because ANYBODY can file an issue, WHO authored it still matters —
288
- # but author-trust drives only (1) the \`--origin-trust\` STAMP on the emitted
289
- # document and (2), via that stamp read by \`intake\`'s dispatch, which PLACEMENT
290
- # default the document lands in. So an untrusted author's task DOCUMENT MERGES to
291
- # \`main\` just like a trusted one when \`intakeIntegration\` is \`merge\`; the
292
- # untrusted safety is the CARRIED stamp (it forces the later BUILD to a code PR)
293
- # plus the placement default, not a forced document PR. "Merge everything" is
294
- # \`intakeIntegration: merge\` (or \`integration: merge\`), independent of the gates
295
- # AND of who filed the issue — so a repo can have autonomy AND merged documents.
293
+ # THE DOCUMENT MODE is \`intakeIntegration\`; AUTHOR-TRUST → PLACEMENT + the STAMP.
294
+ # Both are derived by the item's LOCK job from trusted inputs, never from the
295
+ # agent: the document merge-vs-propose mode is the resolved
296
+ # \`intakeIntegration ?? integration\` in dorfl.json at the base (an
297
+ # operator/config choice, a SINGLE value applied to BOTH the task and spec
298
+ # document, DECOUPLED from the autonomy gates; spec
299
+ # intake-integration-knob-and-specs-land-in-proposed-rename), and the
300
+ # origin-trust stamp comes from THIS run's event: an author outside
301
+ # OWNER/MEMBER/COLLABORATOR (\`comment.author_association\`, else
302
+ # \`issue.author_association\`) is untrusted (ADR
303
+ # untrusted-origin-carries-via-stamp-not-forced-staging). Author-trust drives
304
+ # only (1) the \`originTrust\` STAMP on the emitted document and (2), via that
305
+ # stamp, which PLACEMENT default the document lands in; the untrusted safety is
306
+ # the CARRIED stamp (it forces the later BUILD to a code PR) plus the placement
307
+ # default, not a forced document PR.
296
308
  #
297
309
  # CI runs IN-PLACE (the CI container IS the isolation): NO --isolated/--remote/
298
310
  # registry (laptop-only affordances). The PER-ISSUE concurrency group below
299
311
  # serialises overlapping ticks on the SAME issue; the \`processing\` lock / claim
300
312
  # CAS is the real cross-run serialiser.
301
313
  #
302
- # SAFETY (US #9): the running job is FORBIDDEN from editing the workflows tree
303
- # under .github. It requests NO \`workflows\` permission, so it can never rewrite
304
- # its own triggers.
314
+ # SAFETY (US #9): no job requests a \`workflows\` permission, so none can
315
+ # rewrite its own triggers.
305
316
 
306
317
  name: intake
307
318
 
@@ -324,39 +335,14 @@ on:
324
335
  # triggers landing close together must not run intake on one issue twice at once).
325
336
  # The \`processing\` lock / claim CAS is the real cross-run serialiser; this just
326
337
  # avoids redundant concurrent ticks. Keyed by the issue number so DIFFERENT issues
327
- # still run in parallel.
338
+ # still run in parallel. One intake run carries exactly one issue, so no
339
+ # parallelism slot applies here.
328
340
  concurrency:
329
341
  group: intake-\${{ github.event.issue.number }}
330
342
  cancel-in-progress: false
331
343
 
332
- # NO \`workflows\` permission: the running job can NEVER edit the workflows tree
333
- # under .github (US #9). \`contents: write\` + \`pull-requests: write\` emit/propose
334
- # the artifact; \`issues: write\` posts the clarifying/review comment back into the
335
- # thread (insertion point E). It never rewrites its triggers.
336
- permissions:
337
- contents: write
338
- pull-requests: write
339
- issues: write
340
-
341
- # ── The intake DOCUMENT mode is resolved FROM CONFIG, not carried here ───────
342
- # CI is NOT a special policy surface (ADR ci-config-policy-and-gate-family §5):
343
- # it runs the SAME engine config, resolved through flag > env > per-repo > global
344
- # > default. The SAME dorfl.json the laptop uses applies here. This workflow emits
345
- # NO DORFL_AUTO_BUILD / DORFL_AUTO_TASK line (ADR
346
- # untrusted-origin-carries-via-stamp-not-forced-staging: hardcoding them here made
347
- # the env layer OUTRANK the committed dorfl.json — the shadowing bug). So the env
348
- # layer carries NO config default; the policy step below READS the resolved intake
349
- # DOCUMENT mode \`intakeIntegration ?? integration\` via \`dorfl config --json\` (the
350
- # mechanism \`advance\` already uses), so your committed dorfl.json wins (then the
351
- # global config, then the built-in default \`propose\`). To land intake documents on
352
- # main, set \`intakeIntegration: merge\` (or \`integration: merge\`) in dorfl.json
353
- # (applies everywhere) — NOT by re-running install-ci (ADR §6: install-ci is
354
- # one-time). The autonomy gates \`autoBuild\`/\`autoTask\` are DECOUPLED from the
355
- # document mode (spec intake-integration-knob-and-specs-land-in-proposed-rename):
356
- # they gate autonomy only and are NOT read here. \`intake\` itself is GATE-FREE (the
357
- # explicit invocation is its own authorization); CI READS only the resolved
358
- # \`intakeIntegration\`/\`integration\` to DERIVE the merge-vs-propose document flags
359
- # below (the merge-vs-propose POLICY).
344
+ # Nothing at workflow level: the calling job grants what dorfl-item.yml needs.
345
+ permissions: {}
360
346
 
361
347
  jobs:
362
348
  intake:
@@ -364,124 +350,15 @@ jobs:
364
350
  # comment on a PR also fires \`issue_comment\`; skip those — there is no issue to
365
351
  # intake). \`pull_request\` is absent on a real issue comment.
366
352
  if: \${{ github.event.issue.number && !github.event.issue.pull_request }}
367
- runs-on: ubuntu-latest
368
- steps:
369
- - uses: ${pinnedUses(ACTION_PINS.checkout)}
370
- with:
371
- fetch-depth: 0
372
- - uses: ./.github/actions/dorfl-setup${setupWith}
373
-
374
- - name: derive the intake DOCUMENT mode (intakeIntegration) + the origin-trust stamp (author-trust)
375
- id: policy
376
- # The intake POLICY, executed at runtime — the SAME rule
377
- # \`deriveIntakeFlags\` unit-tests (they cannot desync; the test asserts this
378
- # shell matches the function). The DOCUMENT mode is the resolved
379
- # \`intakeIntegration ?? integration\` (operator/config), DECOUPLED from the
380
- # autonomy gates; author-trust drives ONLY the stamp + placement (ADR
381
- # untrusted-origin-carries-via-stamp-not-forced-staging; spec
382
- # intake-integration-knob-and-specs-land-in-proposed-rename):
383
- # * DOCUMENT mode (spec = task) — config-derived: --merge-* iff the
384
- # resolved intakeIntegration is merge, else --propose-*. A SINGLE
385
- # value applied to BOTH the spec and task document. autoBuild/
386
- # autoTask are NOT read (they gate autonomy only).
387
- # * ORIGIN-TRUST stamp — the ONLY thing author-trust drives: --origin-trust
388
- # untrusted iff the author is not OWNER/MEMBER/COLLABORATOR, which
389
- # \`intake\`'s dispatch reads to select the untrusted PLACEMENT
390
- # default and to force the later BUILD to a code PR.
391
- # author_association comes from the COMMENT on an \`issue_comment\` event,
392
- # else the ISSUE on an \`issues\` event — read straight off the payload, no
393
- # extra API call.
394
- env:
395
- AUTHOR_ASSOCIATION: \${{ github.event.comment.author_association || github.event.issue.author_association }}
396
- run: |
397
- set -euo pipefail
398
-
399
- # Read the RESOLVED intake DOCUMENT mode from the committed config via
400
- # \`dorfl config --json\` (the mechanism \`advance\` already uses), NOT a
401
- # hardcoded DORFL_* env (ADR
402
- # untrusted-origin-carries-via-stamp-not-forced-staging: an env default
403
- # would OUTRANK dorfl.json — the shadowing bug). In-place, so the
404
- # resolution chain reads THIS repo's dorfl.json exactly like the laptop.
405
- # \`intakeIntegration\` is OPTIONAL (unset ⇒ falls back to \`integration\`),
406
- # so read \`.intakeIntegration // .integration\` in ONE jq expression — the
407
- # shell twin of the \`intakeIntegration ?? integration\` the CLI applies.
408
- # The autonomy gates (autoBuild/autoTask) are DECOUPLED from the document
409
- # mode (spec intake-integration-knob-and-specs-land-in-proposed-rename):
410
- # they gate autonomy only and are NOT read here.
411
- config_json="$(dorfl config --json)"
412
- intake_integration="$(echo "\${config_json}" | jq -r '.intakeIntegration // .integration')"
413
-
414
- # DOCUMENT mode: config-derived from the single \`intakeIntegration ??
415
- # integration\` value, applied to BOTH the spec and task document (US #1
416
- # chose one intake knob, not a per-type split). merge ⇒ --merge-*,
417
- # else --propose-*. Author-trust does NOT bite the mode (ADR
418
- # untrusted-origin-carries-via-stamp-not-forced-staging): an untrusted
419
- # author's DOCUMENT merges just like a trusted one; the untrusted safety
420
- # is the stamp + placement below, not a document PR.
421
- if [ "\${intake_integration}" = "merge" ]; then
422
- spec_flag="--merge-spec"
423
- task_flag="--merge-task"
424
- else
425
- spec_flag="--propose-spec"
426
- task_flag="--propose-task"
427
- fi
428
-
429
- # Author-trust: TRUSTED iff OWNER/MEMBER/COLLABORATOR (admin / write-
430
- # collaborator — the whole signal). Anything else (incl. empty) is
431
- # UNTRUSTED. It drives ONLY the origin-trust stamp below (NOT the modes).
432
- trusted="false"
433
- case "\${AUTHOR_ASSOCIATION:-}" in
434
- OWNER|MEMBER|COLLABORATOR) trusted="true" ;;
435
- esac
436
-
437
- # ORIGIN-TRUST stamp — the SOLE thing author-trust drives on the wire
438
- # (task untrusted-origin-forces-build-propose). \`intake\` STAMPS this
439
- # onto the emitted spec/task frontmatter (origin: issue + originTrust:
440
- # <value>); its dispatch reads the stamp to (1) select the untrusted-side
441
- # PLACEMENT default (\`untrusted*LandIn\`) and (2) force the later BUILD of
442
- # an untrusted task to a code PR. It does NOT re-resolve trust (that is
443
- # CI's policy, passed IN). The stamp SURVIVES the merge boundary so a
444
- # later auto-task/auto-build of an untrusted-origin artifact still forces
445
- # a human becomes-code checkpoint (the laundering gap is closed).
446
- if [ "\${trusted}" = "true" ]; then
447
- origin_trust_flag="--origin-trust=trusted"
448
- else
449
- origin_trust_flag="--origin-trust=untrusted"
450
- fi
451
-
452
- echo "spec_flag=\${spec_flag}" >> "\$GITHUB_OUTPUT"
453
- echo "task_flag=\${task_flag}" >> "\$GITHUB_OUTPUT"
454
- echo "origin_trust_flag=\${origin_trust_flag}" >> "\$GITHUB_OUTPUT"
455
- echo "intake policy: intakeIntegration='\${intake_integration}' author_association='\${AUTHOR_ASSOCIATION:-}' trusted=\${trusted} → \${spec_flag} \${task_flag} \${origin_trust_flag}"
456
-
457
- - name: intake the issue (four-outcome dispatch; surfaces the review verdict into the thread)
458
- # In-place in this checkout (no --isolated/--remote): the CI container IS
459
- # the isolation. EXPLICIT \`intake <N>\`, never a bare slug. The per-outcome
460
- # flags carry the (intakeIntegration-derived) document modes + the
461
- # origin-trust stamp derived above (author-trust drives only the stamp +
462
- # placement, not the document merge-vs-propose; the autonomy gates drive
463
- # neither). \`intake\` runs the
464
- # lone-task review/edit loop and posts its findings as questions back into
465
- # THIS issue thread (insertion point E) through the issue-comment seam —
466
- # CI surfaces E by invoking intake; it adds no new review mechanism.
467
- #
468
- # Every value reaches the shell through \`env:\` as DATA and is read back as a
469
- # quoted variable, never as \`\${{ }}\` text spliced into the script
470
- # (GitHub's script-injection guidance). The issue number is an integer and
471
- # the policy outputs are fixed flags today; routing them through env anyway
472
- # keeps "no \`\${{ }}\` inside \`run:\`" a rule with no exceptions.
473
- env:
474
- GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
475
- ISSUE_NUMBER: \${{ github.event.issue.number }}
476
- SPEC_FLAG: \${{ steps.policy.outputs.spec_flag }}
477
- TASK_FLAG: \${{ steps.policy.outputs.task_flag }}
478
- ORIGIN_TRUST_FLAG: \${{ steps.policy.outputs.origin_trust_flag }}
479
- run: |
480
- dorfl intake "\${ISSUE_NUMBER}" \\
481
- "\${SPEC_FLAG}" \\
482
- "\${TASK_FLAG}" \\
483
- "\${ORIGIN_TRUST_FLAG}" \\
484
- --arbiter origin
353
+ # The union of what dorfl-item.yml's jobs request (a called workflow can
354
+ # only narrow this): the lock and apply jobs write (the \`processing\` label,
355
+ # the comment back into the thread = insertion point E, the document PR or
356
+ # merge); apply reads the agent job's check runs (a timeout vs a cancel).
357
+ permissions:
358
+ ${ITEM_WORKFLOW_CALLER_PERMISSIONS}
359
+ uses: ./.github/workflows/dorfl-item.yml
360
+ with:
361
+ item: issue:\${{ github.event.issue.number }}${itemCallSecrets(config, false)}
485
362
  `;
486
363
  }
487
364
 
@@ -525,33 +402,38 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
525
402
  .filter((line) => !/^\s*#/.test(line))
526
403
  .join('\n');
527
404
 
528
- // --- INVOKES `intake <N>` (explicit, four-outcome dispatch), never bare ------
529
- require('invokes-intake', /dorfl intake\b/.test(
405
+ // --- CALLS the per-item workflow with `item: issue:<N>` (decision 10) -------
406
+ require('calls-item-workflow', /\n {4}uses: \.\/\.github\/workflows\/dorfl-item\.yml\n/.test(
530
407
  operative,
531
- ), 'must invoke `dorfl intake <N>` (the four-outcome dispatch engine CI ' +
532
- 'schedules).');
533
- // The issue number rides the explicit positional — never a bare slug, and the
534
- // number comes from the event payload (the issue under intake).
535
- require('intake-explicit-issue-number', /ISSUE_NUMBER:\s*\$\{\{\s*github\.event\.issue\.number\s*\}\}[\s\S]*?dorfl intake "\$\{ISSUE_NUMBER\}"/.test(
408
+ ), 'the intake job must call the per-item workflow ' +
409
+ '(`uses: ./.github/workflows/dorfl-item.yml`: lock, agent, apply).');
410
+ // The issue number rides the explicit `issue:<N>` item id from the event
411
+ // payload (the issue under intake) — never a bare slug.
412
+ require('intake-explicit-issue-number', /\n {6}item: issue:\$\{\{ github\.event\.issue\.number \}\}\n/.test(
536
413
  operative,
537
- ), 'the intake invocation must pass the explicit issue NUMBER ' +
538
- '(`github.event.issue.number`, via the step env as `ISSUE_NUMBER`), never a bare slug.');
539
- // No `${{ }}` spliced into the intake `run:` script (script injection): the
540
- // number and the policy flags reach the shell through the step `env:`.
541
- require('intake-args-not-spliced-into-run', !/dorfl intake "?\$\{\{/.test(
414
+ ), 'the item must be the explicit `issue:<N>` id ' +
415
+ '(`item: issue:${{ github.event.issue.number }}`).');
416
+ // No agent verb runs in THIS workflow: the decision agent runs in the called
417
+ // workflow's agent job (read-only token).
418
+ require('no-agent-verbs', !/dorfl (?:do|advance|intake|run)\b/.test(
542
419
  operative,
543
- ) &&
544
- !/"\$\{\{\s*steps\.policy\.outputs\./.test(
545
- operative,
546
- ), 'the intake invocation must NOT interpolate `${{ github.event.issue.number }}` ' +
547
- 'or `${{ steps.policy.outputs.* }}` into its `run:` script: pass them through ' +
548
- 'the step `env:` and quote the variables.');
549
- // CI owns ONLY the trigger/policy/delivery — it must NOT invoke a build/task
550
- // verb (that is the build/task tick), nor re-implement the transform.
551
- require('no-build-verbs', !/dorfl (?:do|advance)\b/.test(
420
+ ), 'the intake workflow must run no dorfl verb itself: `intake` runs in the ' +
421
+ "called workflow's lock, agent and apply jobs.");
422
+ // The caller grants the union of what the called jobs request.
423
+ require('caller-grants-item-scopes', /\n {4}permissions:\n {6}contents: write\n {6}issues: write\n {6}pull-requests: write\n {6}actions: read\n {6}checks: read\n/.test(
424
+ operative,
425
+ ), 'the calling job must grant every scope a job of dorfl-item.yml requests ' +
426
+ '(`contents`, `issues`, `pull-requests: write`; `actions`, `checks: read`).');
427
+ require('workflow-permissions-empty', /^permissions: \{\}$/m.test(
428
+ text,
429
+ ), 'the workflow must grant nothing at workflow level (`permissions: {}`).');
430
+ require('secrets-explicit', !/secrets:\s*inherit\b/.test(
552
431
  operative,
553
- ), 'the intake workflow must invoke ONLY `intake` (+ derive the policy), not a ' +
554
- 'build/task verb — CI owns the trigger + merge policy, not the transform.');
432
+ ), 'secrets reach the called workflow explicitly, never `secrets: inherit`.');
433
+ require('no-pr-identity-token', !/DORFL_GH_TOKEN/.test(
434
+ operative,
435
+ ), 'intake keeps the built-in token for its writes (no DORFL_GH_TOKEN is ' +
436
+ 'passed to the called workflow).');
555
437
 
556
438
  // --- TRIGGERS: issues opened + issue_comment created + the label ------------
557
439
  require('trigger-issues-opened', /\bissues:\s*[\s\S]*?types:\s*[\s\S]*?-\s*opened\b/.test(
@@ -564,10 +446,12 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
564
446
  require('trigger-label', /\bissues:\s*[\s\S]*?types:\s*[\s\S]*?-\s*labeled\b/.test(
565
447
  text,
566
448
  ), 'must trigger on a label (`on.issues.types: [labeled]`).');
449
+ // A comment on a PR also fires `issue_comment`: there is no issue to intake.
450
+ require('skips-pull-request-comments', /if: \$\{\{ github\.event\.issue\.number && !github\.event\.issue\.pull_request \}\}/.test(
451
+ operative,
452
+ ), 'the intake job must skip a comment on a pull request (no issue to intake).');
567
453
 
568
454
  // --- Decision 2: a CREATED comment triggers; an EDITED comment does NOT ------
569
- // The `issue_comment` trigger must NOT list `edited` (no edit-detection); and
570
- // there must be no `updated_at`/body-hash edit-tracking wiring.
571
455
  require('no-comment-edited-trigger', !/issue_comment:\s*[\s\S]*?types:\s*[\s\S]*?-\s*edited\b/.test(
572
456
  text,
573
457
  ), 'the `issue_comment` trigger must NOT include `edited` (Decision 2: no ' +
@@ -575,122 +459,37 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
575
459
  require('no-edit-tracking', !/updated_at|body-hash|bodyHash/.test(
576
460
  operative,
577
461
  ), 'must NOT implement `updated_at` / body-hash edit-tracking (Decision 2).');
578
- // The "post a NEW comment to signal an edit" CONVENTION must be documented in
579
- // the workflow so a human knows how to re-trigger after editing a comment.
580
462
  require('documents-new-comment-convention', /post a NEW comment/i.test(
581
463
  text,
582
464
  ), 'must DOCUMENT the "post a new comment to signal an edit" convention ' +
583
465
  '(Decision 2) so a human knows how to drive re-evaluation.');
584
466
 
585
- // --- AUTHOR-TRUST → per-outcome flags (Decision 1) --------------------------
586
- // The workflow must READ author_association off the event payload (no extra API).
587
- require('reads-author-association', /author_association/.test(
588
- text,
589
- ), 'must read `author_association` off the event payload to compose author-' +
590
- 'trust into the merge-vs-propose policy (Decision 1; no extra API call).');
591
- // Trust = OWNER/MEMBER/COLLABORATOR (the whole signal).
592
- require('trust-owner-member-collaborator', /OWNER\|MEMBER\|COLLABORATOR|OWNER[\s\S]{0,40}MEMBER[\s\S]{0,40}COLLABORATOR/.test(
593
- text,
594
- ), 'author-trust must be OWNER/MEMBER/COLLABORATOR (admin / write-collaborator ' +
595
- '— the whole signal; Decision 1).');
596
- // The DOCUMENT mode is intakeIntegration-DERIVED (spec
597
- // intake-integration-knob-and-specs-land-in-proposed-rename; the autonomy gates
598
- // no longer bite it): the derivation must be able to emit both task modes
599
- // (merge iff the resolved intakeIntegration is merge, else propose).
600
- require('derives-propose-task', /--propose-task\b/.test(
601
- operative,
602
- ), 'the policy derivation must be able to emit `--propose-task` (the ' +
603
- 'intakeIntegration-propose path).');
604
- require('derives-merge-task', /--merge-task\b/.test(
605
- operative,
606
- ), 'the policy derivation must be able to emit `--merge-task` (the ' +
607
- 'intakeIntegration-merge path; a task DOCUMENT merges regardless of the ' +
608
- 'autonomy gates or author-trust).');
609
- // --merge-spec is emitted when the resolved intakeIntegration is merge (the
610
- // SAME single value as the task; the gates do not bite it).
611
- require('derives-merge-spec', /--merge-spec\b/.test(
612
- operative,
613
- ), 'the policy derivation must be able to emit `--merge-spec` (the ' +
614
- 'intakeIntegration-merge path).');
615
- require('derives-propose-spec', /--propose-spec\b/.test(
616
- operative,
617
- ), 'the policy derivation must be able to emit `--propose-spec` (the ' +
618
- 'intakeIntegration-propose path).');
619
- // ORIGIN-TRUST stamp (task untrusted-origin-forces-build-propose): the shell
620
- // must derive `--origin-trust <trusted|untrusted>` from the author-trust case
621
- // (independent of the document mode) and pass it to `intake` so the emitted
622
- // artifact is stamped (the stamp is the SOLE thing author-trust drives).
623
- require('derives-origin-trust-untrusted', /--origin-trust=untrusted\b/.test(
624
- operative,
625
- ), 'the policy derivation must emit `--origin-trust=untrusted` for a non-trusted ' +
626
- 'author (so the emitted artifact is stamped untrusted; task ' +
627
- 'untrusted-origin-forces-build-propose).');
628
- require('derives-origin-trust-trusted', /--origin-trust=trusted\b/.test(
629
- operative,
630
- ), 'the policy derivation must emit `--origin-trust=trusted` for a trusted author.');
631
- require('passes-origin-trust-to-intake', /steps\.policy\.outputs\.origin_trust_flag/.test(
632
- operative,
633
- ), 'the intake invocation must pass the derived `--origin-trust` flag (the ' +
634
- 'stamp must reach `dorfl intake`).');
635
- // The derivation must read the RESOLVED intake DOCUMENT mode via `dorfl config
636
- // --json` (the mechanism `advance` uses), NOT a hardcoded DORFL_* env (ADR
637
- // untrusted-origin-carries-via-stamp-not-forced-staging — the shadowing bug).
638
- require('reads-config-json', /dorfl config --json/.test(
639
- operative,
640
- ), 'the policy derivation must read the resolved config via ' +
641
- '`dorfl config --json` (as `advance` does), so a committed `dorfl.json` ' +
642
- 'mode is honored in CI (not shadowed by a hardcoded env).');
643
- // The DOCUMENT mode is the resolved `intakeIntegration ?? integration` (spec
644
- // intake-integration-knob-and-specs-land-in-proposed-rename): the derivation
645
- // must read `.intakeIntegration` with a `.integration` FALLBACK (the shell twin
646
- // of `intakeIntegration ?? integration`), NOT the autonomy gates. Pin BOTH the
647
- // key and the fallback so a regression to a gate-derived mode is caught.
648
- require('reads-intake-integration', /\.intakeIntegration\b/.test(
649
- operative,
650
- ), 'the policy derivation must read the resolved `intakeIntegration` mode (jq ' +
651
- '`.intakeIntegration` off `dorfl config --json`) for the document mode.');
652
- require('intake-integration-falls-back-to-integration', /\.intakeIntegration\s*\/\/\s*\.integration/.test(
653
- operative,
654
- ), 'the policy derivation must fall back to `.integration` when ' +
655
- '`intakeIntegration` is unset (jq `.intakeIntegration // .integration`, the ' +
656
- 'shell twin of `intakeIntegration ?? integration`).');
657
- // The DOCUMENT mode must NOT be derived from the autonomy gates (spec
658
- // intake-integration-knob-and-specs-land-in-proposed-rename: autoBuild/autoTask
659
- // gate autonomy ONLY, never the document PR-mode). The mode branch must not read
660
- // `.autoBuild` / `.autoTask` off the config json.
661
- require('mode-not-gate-derived', !/\.auto(?:Build|Task)\b/.test(
662
- operative,
663
- ), 'the intake DOCUMENT mode must be derived from `intakeIntegration ?? ' +
664
- 'integration`, NOT the autonomy gates — the derivation must not read ' +
665
- '`.autoBuild` / `.autoTask` (they gate autonomy only; spec ' +
666
- 'intake-integration-knob-and-specs-land-in-proposed-rename).');
667
- // ANTI-REGRESSION (ADR untrusted-origin-carries-via-stamp-not-forced-staging;
668
- // spec US #12): the workflow must NOT emit a `DORFL_AUTO_BUILD:` /
669
- // `DORFL_AUTO_TASK:` env ASSIGNMENT. The env layer OUTRANKS per-repo config, so
670
- // a hardcoded default here SHADOWS the committed `dorfl.json` gates (the bug this
671
- // task fixes). Mirrors `advance-lifecycle-template.ts`'s `no-gate-env-auto-build`
672
- // / `no-gate-env-auto-task`. Checked over the OPERATIVE (non-comment) lines so
673
- // the header comment that NAMES these keys is not a false positive.
467
+ // --- The policy is derived in the lock job, from trusted inputs --------------
468
+ // The document mode and the origin-trust stamp are derived by the called
469
+ // workflow's LOCK job (`ci-phase-intake.ts`), never by the agent: this
470
+ // workflow must not derive them itself any more, nor carry a gate env.
674
471
  require('no-gate-env-auto-build', !/DORFL_AUTO_BUILD\s*:/.test(
675
472
  operative,
676
473
  ), 'the workflow must NOT emit a `DORFL_AUTO_BUILD:` env assignment (env ' +
677
- 'carries no defaults; the gate is resolved from per-repo config / built-in ' +
678
- 'default — else the env SHADOWS the committed dorfl.json).');
474
+ 'carries no defaults — else the env SHADOWS the committed dorfl.json).');
679
475
  require('no-gate-env-auto-task', !/DORFL_AUTO_TASK\s*:/.test(
680
476
  operative,
681
477
  ), 'the workflow must NOT emit a `DORFL_AUTO_TASK:` env assignment (env ' +
682
- 'carries no defaults; the gate is resolved from per-repo config / built-in ' +
683
- 'default — else the env SHADOWS the committed dorfl.json).');
478
+ 'carries no defaults — else the env SHADOWS the committed dorfl.json).');
479
+ require('documents-policy-derivation', /OWNER\/MEMBER\/COLLABORATOR/.test(
480
+ text,
481
+ ) &&
482
+ /intakeIntegration \?\? integration/.test(
483
+ text,
484
+ ), "must DOCUMENT where the policy comes from: the lock job's origin trust " +
485
+ '(OWNER/MEMBER/COLLABORATOR, from the event) and document mode ' +
486
+ '(`intakeIntegration ?? integration`, from dorfl.json at the base).');
684
487
 
685
488
  // --- Insertion point E: the issue-thread review surface ---------------------
686
- // E is REUSED via `intake` (which runs the lone-task review and posts to the
687
- // thread). The workflow must request `issues: write` so that comment can land.
688
489
  require('issues-write-permission', /\bissues:\s*write\b/.test(
689
490
  operative,
690
- ), 'must request `issues: write` so the review verdict / clarifying question ' +
491
+ ), 'must grant `issues: write` so the review verdict / clarifying question ' +
691
492
  'can be posted back into the issue thread (insertion point E).');
692
- // It must NOT route the review verdict through the PR-comment seam — E posts to
693
- // the ISSUE (postIssueComment by number), NOT the PR (postPRComment by url).
694
493
  require('no-pr-comment-seam', !/postPRComment\b/.test(
695
494
  operative,
696
495
  ), 'insertion point E posts to the ISSUE thread (postIssueComment by number), ' +
@@ -718,15 +517,14 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
718
517
  text,
719
518
  ), 'the running job must request NO `workflows` permission (US #9: it can ' +
720
519
  'never edit `.github/workflows/**` / rewrite its own triggers).');
520
+ // Calling a reusable workflow (`uses: ./.github/workflows/...`) is not a
521
+ // step touching the tree; anything else naming it is.
721
522
  require('never-edits-dot-github-workflows', !/\.github\/workflows\//.test(
722
- operative,
523
+ operative
524
+ .split('\n')
525
+ .filter((line) => !/^\s*uses: \.\/\.github\/workflows\//.test(line))
526
+ .join('\n'),
723
527
  ), 'no emitted job step may touch `.github/workflows/**` (US #9).');
724
528
 
725
- // --- Wires the SHARED composite setup action -------------------------------
726
- require('uses-shared-setup-action', /uses:\s*\.\/\.github\/actions\/dorfl-setup\b/.test(
727
- text,
728
- ), 'the job must wire the shared composite setup action ' +
729
- '(`./.github/actions/dorfl-setup`, emitted by the core task).');
730
-
731
529
  return {ok: problems.length === 0, problems};
732
530
  }