dorfl 0.14.2 → 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 +309 -489
  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 +46 -14
  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 +123 -0
  161. package/dist/install-ci-action-pins.d.ts.map +1 -0
  162. package/dist/install-ci-action-pins.js +190 -0
  163. package/dist/install-ci-action-pins.js.map +1 -0
  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 +155 -37
  173. package/dist/install-ci-core.d.ts.map +1 -1
  174. package/dist/install-ci-core.js +304 -200
  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 -292
  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 +8 -4
  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 +375 -538
  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 +52 -15
  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 +209 -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 +388 -233
  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 -327
  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 +8 -4
  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;
@@ -69,7 +76,10 @@
69
76
 
70
77
  import {brand} from './brand.js';
71
78
  import type {ResolvedCIConfig} from './install-ci-core.js';
72
- import {providerSecretsWithBlock} from './install-ci-core.js';
79
+ import {
80
+ ITEM_WORKFLOW_CALLER_PERMISSIONS,
81
+ itemCallSecrets,
82
+ } from './dorfl-item-template.js';
73
83
 
74
84
  /** The capability id (the registry key + the emitted workflow file stem). */
75
85
  export const INTAKE_TRIGGER_CAPABILITY_ID = 'intake';
@@ -161,9 +171,10 @@ export interface IntakeIntegrationFlags {
161
171
  * DERIVE the per-outcome file-emit modes + the origin-trust stamp — CI's intake
162
172
  * POLICY (ADR `untrusted-origin-carries-via-stamp-not-forced-staging`; spec
163
173
  * `intake-integration-knob-and-specs-land-in-proposed-rename` US #1/#2). This is
164
- * the load-bearing pure logic the workflow encodes at runtime (it reads the
165
- * resolved `intakeIntegration ?? integration` via `dorfl config --json` for the
166
- * 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`):
167
178
  *
168
179
  * - **DOCUMENT mode (spec = task)** — the resolved `intakeIntegration` value, a
169
180
  * SINGLE mode applied to BOTH the spec and task document: `merge` ⇒
@@ -232,27 +243,22 @@ export function isAuthorTrusted(
232
243
 
233
244
  /**
234
245
  * Generate the intake-trigger workflow YAML. Deterministic: the same config
235
- * produces byte-identical output. The workflow is a FIXED shell (ADR §6: all
236
- * policy is env/config, so the artifact carries no config-derived policy beyond
237
- * the env-block scaffolding) — `config` is accepted for parity with the
238
- * `CapabilityEmitter` seam and future per-config wiring, but the intake-trigger
239
- * shape itself is config-independent.
246
+ * produces byte-identical output.
240
247
  *
241
- * The per-outcome FLAGS are DERIVED AT RUNTIME by a `bash` step that mirrors
242
- * {@link deriveIntakeFlags}: it reads the resolved `intakeIntegration ??
243
- * integration` via `dorfl config --json` to set the (config-derived) DOCUMENT
244
- * mode `--merge-spec`/`--propose-spec` + `--merge-task`/`--propose-task` (a SINGLE
245
- * mode applied to both), and the event's `author_association` to set ONLY the
246
- * `--origin-trust` STAMP (which carries the placement + build-PR consequence). The
247
- * autonomy gates (`autoBuild`/`autoTask`) are NOT read for the document mode (they
248
- * mean only "may an agent act autonomously"). The same rule that
249
- * {@link deriveIntakeFlags} unit-tests is what the workflow executes — they cannot
250
- * 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.
251
260
  */
252
261
  export function generateIntakeWorkflow(config: ResolvedCIConfig): string {
253
- // `intake <N>` runs the prompt→verdict decision (the agent), so it needs the
254
- // provider secret(s) forwarded to `$GITHUB_ENV` by the setup action.
255
- const setupWith = providerSecretsWithBlock(config);
256
262
  return `\
257
263
  # dorfl — the ISSUE INTAKE trigger in CI (capability D: consider incoming
258
264
  # issues → task/spec, PLUS insertion point E: surface the review verdict into the
@@ -267,6 +273,14 @@ export function generateIntakeWorkflow(config: ResolvedCIConfig): string {
267
273
  # findings as questions back into THIS issue thread via the issue-comment seam (insertion
268
274
  # point E) — REUSED, not a new review mechanism.
269
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
+ #
270
284
  # TRIGGERS (capability D): an OPENED issue, a CREATED issue comment, and the
271
285
  # \`${INTAKE_TRIGGER_LABEL}\` label. A CREATED comment is the (re-)evaluation
272
286
  # trigger; an EDITED comment is deliberately NOT a trigger (the ID-based
@@ -276,31 +290,29 @@ export function generateIntakeWorkflow(config: ResolvedCIConfig): string {
276
290
  # re-evaluation (a fresh id the watermark catches). There is NO edit-detection /
277
291
  # \`updated_at\` / body-hash tracking.
278
292
  #
279
- # THE DOCUMENT MODE is \`intakeIntegration\`; AUTHOR-TRUST → PLACEMENT + the STAMP:
280
- # the intake DOCUMENT merge-vs-propose mode is the resolved \`intakeIntegration ??
281
- # integration\` (an operator/config choice, a SINGLE value applied to BOTH the task
282
- # and spec document), DECOUPLED from the autonomy gates (spec
283
- # intake-integration-knob-and-specs-land-in-proposed-rename; ADR
284
- # untrusted-origin-carries-via-stamp-not-forced-staging). \`autoBuild\`/\`autoTask\`
285
- # no longer decide the document mode (they mean only "may an agent act
286
- # autonomously"). Because ANYBODY can file an issue, WHO authored it still matters —
287
- # but author-trust drives only (1) the \`--origin-trust\` STAMP on the emitted
288
- # document and (2), via that stamp read by \`intake\`'s dispatch, which PLACEMENT
289
- # default the document lands in. So an untrusted author's task DOCUMENT MERGES to
290
- # \`main\` just like a trusted one when \`intakeIntegration\` is \`merge\`; the
291
- # untrusted safety is the CARRIED stamp (it forces the later BUILD to a code PR)
292
- # plus the placement default, not a forced document PR. "Merge everything" is
293
- # \`intakeIntegration: merge\` (or \`integration: merge\`), independent of the gates
294
- # 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.
295
308
  #
296
309
  # CI runs IN-PLACE (the CI container IS the isolation): NO --isolated/--remote/
297
310
  # registry (laptop-only affordances). The PER-ISSUE concurrency group below
298
311
  # serialises overlapping ticks on the SAME issue; the \`processing\` lock / claim
299
312
  # CAS is the real cross-run serialiser.
300
313
  #
301
- # SAFETY (US #9): the running job is FORBIDDEN from editing the workflows tree
302
- # under .github. It requests NO \`workflows\` permission, so it can never rewrite
303
- # its own triggers.
314
+ # SAFETY (US #9): no job requests a \`workflows\` permission, so none can
315
+ # rewrite its own triggers.
304
316
 
305
317
  name: intake
306
318
 
@@ -323,39 +335,14 @@ on:
323
335
  # triggers landing close together must not run intake on one issue twice at once).
324
336
  # The \`processing\` lock / claim CAS is the real cross-run serialiser; this just
325
337
  # avoids redundant concurrent ticks. Keyed by the issue number so DIFFERENT issues
326
- # still run in parallel.
338
+ # still run in parallel. One intake run carries exactly one issue, so no
339
+ # parallelism slot applies here.
327
340
  concurrency:
328
341
  group: intake-\${{ github.event.issue.number }}
329
342
  cancel-in-progress: false
330
343
 
331
- # NO \`workflows\` permission: the running job can NEVER edit the workflows tree
332
- # under .github (US #9). \`contents: write\` + \`pull-requests: write\` emit/propose
333
- # the artifact; \`issues: write\` posts the clarifying/review comment back into the
334
- # thread (insertion point E). It never rewrites its triggers.
335
- permissions:
336
- contents: write
337
- pull-requests: write
338
- issues: write
339
-
340
- # ── The intake DOCUMENT mode is resolved FROM CONFIG, not carried here ───────
341
- # CI is NOT a special policy surface (ADR ci-config-policy-and-gate-family §5):
342
- # it runs the SAME engine config, resolved through flag > env > per-repo > global
343
- # > default. The SAME dorfl.json the laptop uses applies here. This workflow emits
344
- # NO DORFL_AUTO_BUILD / DORFL_AUTO_TASK line (ADR
345
- # untrusted-origin-carries-via-stamp-not-forced-staging: hardcoding them here made
346
- # the env layer OUTRANK the committed dorfl.json — the shadowing bug). So the env
347
- # layer carries NO config default; the policy step below READS the resolved intake
348
- # DOCUMENT mode \`intakeIntegration ?? integration\` via \`dorfl config --json\` (the
349
- # mechanism \`advance\` already uses), so your committed dorfl.json wins (then the
350
- # global config, then the built-in default \`propose\`). To land intake documents on
351
- # main, set \`intakeIntegration: merge\` (or \`integration: merge\`) in dorfl.json
352
- # (applies everywhere) — NOT by re-running install-ci (ADR §6: install-ci is
353
- # one-time). The autonomy gates \`autoBuild\`/\`autoTask\` are DECOUPLED from the
354
- # document mode (spec intake-integration-knob-and-specs-land-in-proposed-rename):
355
- # they gate autonomy only and are NOT read here. \`intake\` itself is GATE-FREE (the
356
- # explicit invocation is its own authorization); CI READS only the resolved
357
- # \`intakeIntegration\`/\`integration\` to DERIVE the merge-vs-propose document flags
358
- # below (the merge-vs-propose POLICY).
344
+ # Nothing at workflow level: the calling job grants what dorfl-item.yml needs.
345
+ permissions: {}
359
346
 
360
347
  jobs:
361
348
  intake:
@@ -363,124 +350,15 @@ jobs:
363
350
  # comment on a PR also fires \`issue_comment\`; skip those — there is no issue to
364
351
  # intake). \`pull_request\` is absent on a real issue comment.
365
352
  if: \${{ github.event.issue.number && !github.event.issue.pull_request }}
366
- runs-on: ubuntu-latest
367
- steps:
368
- - uses: actions/checkout@v5
369
- with:
370
- fetch-depth: 0
371
- - uses: ./.github/actions/dorfl-setup${setupWith}
372
-
373
- - name: derive the intake DOCUMENT mode (intakeIntegration) + the origin-trust stamp (author-trust)
374
- id: policy
375
- # The intake POLICY, executed at runtime — the SAME rule
376
- # \`deriveIntakeFlags\` unit-tests (they cannot desync; the test asserts this
377
- # shell matches the function). The DOCUMENT mode is the resolved
378
- # \`intakeIntegration ?? integration\` (operator/config), DECOUPLED from the
379
- # autonomy gates; author-trust drives ONLY the stamp + placement (ADR
380
- # untrusted-origin-carries-via-stamp-not-forced-staging; spec
381
- # intake-integration-knob-and-specs-land-in-proposed-rename):
382
- # * DOCUMENT mode (spec = task) — config-derived: --merge-* iff the
383
- # resolved intakeIntegration is merge, else --propose-*. A SINGLE
384
- # value applied to BOTH the spec and task document. autoBuild/
385
- # autoTask are NOT read (they gate autonomy only).
386
- # * ORIGIN-TRUST stamp — the ONLY thing author-trust drives: --origin-trust
387
- # untrusted iff the author is not OWNER/MEMBER/COLLABORATOR, which
388
- # \`intake\`'s dispatch reads to select the untrusted PLACEMENT
389
- # default and to force the later BUILD to a code PR.
390
- # author_association comes from the COMMENT on an \`issue_comment\` event,
391
- # else the ISSUE on an \`issues\` event — read straight off the payload, no
392
- # extra API call.
393
- env:
394
- AUTHOR_ASSOCIATION: \${{ github.event.comment.author_association || github.event.issue.author_association }}
395
- run: |
396
- set -euo pipefail
397
-
398
- # Read the RESOLVED intake DOCUMENT mode from the committed config via
399
- # \`dorfl config --json\` (the mechanism \`advance\` already uses), NOT a
400
- # hardcoded DORFL_* env (ADR
401
- # untrusted-origin-carries-via-stamp-not-forced-staging: an env default
402
- # would OUTRANK dorfl.json — the shadowing bug). In-place, so the
403
- # resolution chain reads THIS repo's dorfl.json exactly like the laptop.
404
- # \`intakeIntegration\` is OPTIONAL (unset ⇒ falls back to \`integration\`),
405
- # so read \`.intakeIntegration // .integration\` in ONE jq expression — the
406
- # shell twin of the \`intakeIntegration ?? integration\` the CLI applies.
407
- # The autonomy gates (autoBuild/autoTask) are DECOUPLED from the document
408
- # mode (spec intake-integration-knob-and-specs-land-in-proposed-rename):
409
- # they gate autonomy only and are NOT read here.
410
- config_json="$(dorfl config --json)"
411
- intake_integration="$(echo "\${config_json}" | jq -r '.intakeIntegration // .integration')"
412
-
413
- # DOCUMENT mode: config-derived from the single \`intakeIntegration ??
414
- # integration\` value, applied to BOTH the spec and task document (US #1
415
- # chose one intake knob, not a per-type split). merge ⇒ --merge-*,
416
- # else --propose-*. Author-trust does NOT bite the mode (ADR
417
- # untrusted-origin-carries-via-stamp-not-forced-staging): an untrusted
418
- # author's DOCUMENT merges just like a trusted one; the untrusted safety
419
- # is the stamp + placement below, not a document PR.
420
- if [ "\${intake_integration}" = "merge" ]; then
421
- spec_flag="--merge-spec"
422
- task_flag="--merge-task"
423
- else
424
- spec_flag="--propose-spec"
425
- task_flag="--propose-task"
426
- fi
427
-
428
- # Author-trust: TRUSTED iff OWNER/MEMBER/COLLABORATOR (admin / write-
429
- # collaborator — the whole signal). Anything else (incl. empty) is
430
- # UNTRUSTED. It drives ONLY the origin-trust stamp below (NOT the modes).
431
- trusted="false"
432
- case "\${AUTHOR_ASSOCIATION:-}" in
433
- OWNER|MEMBER|COLLABORATOR) trusted="true" ;;
434
- esac
435
-
436
- # ORIGIN-TRUST stamp — the SOLE thing author-trust drives on the wire
437
- # (task untrusted-origin-forces-build-propose). \`intake\` STAMPS this
438
- # onto the emitted spec/task frontmatter (origin: issue + originTrust:
439
- # <value>); its dispatch reads the stamp to (1) select the untrusted-side
440
- # PLACEMENT default (\`untrusted*LandIn\`) and (2) force the later BUILD of
441
- # an untrusted task to a code PR. It does NOT re-resolve trust (that is
442
- # CI's policy, passed IN). The stamp SURVIVES the merge boundary so a
443
- # later auto-task/auto-build of an untrusted-origin artifact still forces
444
- # a human becomes-code checkpoint (the laundering gap is closed).
445
- if [ "\${trusted}" = "true" ]; then
446
- origin_trust_flag="--origin-trust=trusted"
447
- else
448
- origin_trust_flag="--origin-trust=untrusted"
449
- fi
450
-
451
- echo "spec_flag=\${spec_flag}" >> "\$GITHUB_OUTPUT"
452
- echo "task_flag=\${task_flag}" >> "\$GITHUB_OUTPUT"
453
- echo "origin_trust_flag=\${origin_trust_flag}" >> "\$GITHUB_OUTPUT"
454
- echo "intake policy: intakeIntegration='\${intake_integration}' author_association='\${AUTHOR_ASSOCIATION:-}' trusted=\${trusted} → \${spec_flag} \${task_flag} \${origin_trust_flag}"
455
-
456
- - name: intake the issue (four-outcome dispatch; surfaces the review verdict into the thread)
457
- # In-place in this checkout (no --isolated/--remote): the CI container IS
458
- # the isolation. EXPLICIT \`intake <N>\`, never a bare slug. The per-outcome
459
- # flags carry the (intakeIntegration-derived) document modes + the
460
- # origin-trust stamp derived above (author-trust drives only the stamp +
461
- # placement, not the document merge-vs-propose; the autonomy gates drive
462
- # neither). \`intake\` runs the
463
- # lone-task review/edit loop and posts its findings as questions back into
464
- # THIS issue thread (insertion point E) through the issue-comment seam —
465
- # CI surfaces E by invoking intake; it adds no new review mechanism.
466
- #
467
- # Every value reaches the shell through \`env:\` as DATA and is read back as a
468
- # quoted variable, never as \`\${{ }}\` text spliced into the script
469
- # (GitHub's script-injection guidance). The issue number is an integer and
470
- # the policy outputs are fixed flags today; routing them through env anyway
471
- # keeps "no \`\${{ }}\` inside \`run:\`" a rule with no exceptions.
472
- env:
473
- GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
474
- ISSUE_NUMBER: \${{ github.event.issue.number }}
475
- SPEC_FLAG: \${{ steps.policy.outputs.spec_flag }}
476
- TASK_FLAG: \${{ steps.policy.outputs.task_flag }}
477
- ORIGIN_TRUST_FLAG: \${{ steps.policy.outputs.origin_trust_flag }}
478
- run: |
479
- dorfl intake "\${ISSUE_NUMBER}" \\
480
- "\${SPEC_FLAG}" \\
481
- "\${TASK_FLAG}" \\
482
- "\${ORIGIN_TRUST_FLAG}" \\
483
- --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)}
484
362
  `;
485
363
  }
486
364
 
@@ -524,33 +402,38 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
524
402
  .filter((line) => !/^\s*#/.test(line))
525
403
  .join('\n');
526
404
 
527
- // --- INVOKES `intake <N>` (explicit, four-outcome dispatch), never bare ------
528
- 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(
529
407
  operative,
530
- ), 'must invoke `dorfl intake <N>` (the four-outcome dispatch engine CI ' +
531
- 'schedules).');
532
- // The issue number rides the explicit positional — never a bare slug, and the
533
- // number comes from the event payload (the issue under intake).
534
- 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(
535
413
  operative,
536
- ), 'the intake invocation must pass the explicit issue NUMBER ' +
537
- '(`github.event.issue.number`, via the step env as `ISSUE_NUMBER`), never a bare slug.');
538
- // No `${{ }}` spliced into the intake `run:` script (script injection): the
539
- // number and the policy flags reach the shell through the step `env:`.
540
- 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(
541
419
  operative,
542
- ) &&
543
- !/"\$\{\{\s*steps\.policy\.outputs\./.test(
544
- operative,
545
- ), 'the intake invocation must NOT interpolate `${{ github.event.issue.number }}` ' +
546
- 'or `${{ steps.policy.outputs.* }}` into its `run:` script: pass them through ' +
547
- 'the step `env:` and quote the variables.');
548
- // CI owns ONLY the trigger/policy/delivery — it must NOT invoke a build/task
549
- // verb (that is the build/task tick), nor re-implement the transform.
550
- 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(
551
431
  operative,
552
- ), 'the intake workflow must invoke ONLY `intake` (+ derive the policy), not a ' +
553
- '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).');
554
437
 
555
438
  // --- TRIGGERS: issues opened + issue_comment created + the label ------------
556
439
  require('trigger-issues-opened', /\bissues:\s*[\s\S]*?types:\s*[\s\S]*?-\s*opened\b/.test(
@@ -563,10 +446,12 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
563
446
  require('trigger-label', /\bissues:\s*[\s\S]*?types:\s*[\s\S]*?-\s*labeled\b/.test(
564
447
  text,
565
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).');
566
453
 
567
454
  // --- Decision 2: a CREATED comment triggers; an EDITED comment does NOT ------
568
- // The `issue_comment` trigger must NOT list `edited` (no edit-detection); and
569
- // there must be no `updated_at`/body-hash edit-tracking wiring.
570
455
  require('no-comment-edited-trigger', !/issue_comment:\s*[\s\S]*?types:\s*[\s\S]*?-\s*edited\b/.test(
571
456
  text,
572
457
  ), 'the `issue_comment` trigger must NOT include `edited` (Decision 2: no ' +
@@ -574,122 +459,37 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
574
459
  require('no-edit-tracking', !/updated_at|body-hash|bodyHash/.test(
575
460
  operative,
576
461
  ), 'must NOT implement `updated_at` / body-hash edit-tracking (Decision 2).');
577
- // The "post a NEW comment to signal an edit" CONVENTION must be documented in
578
- // the workflow so a human knows how to re-trigger after editing a comment.
579
462
  require('documents-new-comment-convention', /post a NEW comment/i.test(
580
463
  text,
581
464
  ), 'must DOCUMENT the "post a new comment to signal an edit" convention ' +
582
465
  '(Decision 2) so a human knows how to drive re-evaluation.');
583
466
 
584
- // --- AUTHOR-TRUST → per-outcome flags (Decision 1) --------------------------
585
- // The workflow must READ author_association off the event payload (no extra API).
586
- require('reads-author-association', /author_association/.test(
587
- text,
588
- ), 'must read `author_association` off the event payload to compose author-' +
589
- 'trust into the merge-vs-propose policy (Decision 1; no extra API call).');
590
- // Trust = OWNER/MEMBER/COLLABORATOR (the whole signal).
591
- require('trust-owner-member-collaborator', /OWNER\|MEMBER\|COLLABORATOR|OWNER[\s\S]{0,40}MEMBER[\s\S]{0,40}COLLABORATOR/.test(
592
- text,
593
- ), 'author-trust must be OWNER/MEMBER/COLLABORATOR (admin / write-collaborator ' +
594
- '— the whole signal; Decision 1).');
595
- // The DOCUMENT mode is intakeIntegration-DERIVED (spec
596
- // intake-integration-knob-and-specs-land-in-proposed-rename; the autonomy gates
597
- // no longer bite it): the derivation must be able to emit both task modes
598
- // (merge iff the resolved intakeIntegration is merge, else propose).
599
- require('derives-propose-task', /--propose-task\b/.test(
600
- operative,
601
- ), 'the policy derivation must be able to emit `--propose-task` (the ' +
602
- 'intakeIntegration-propose path).');
603
- require('derives-merge-task', /--merge-task\b/.test(
604
- operative,
605
- ), 'the policy derivation must be able to emit `--merge-task` (the ' +
606
- 'intakeIntegration-merge path; a task DOCUMENT merges regardless of the ' +
607
- 'autonomy gates or author-trust).');
608
- // --merge-spec is emitted when the resolved intakeIntegration is merge (the
609
- // SAME single value as the task; the gates do not bite it).
610
- require('derives-merge-spec', /--merge-spec\b/.test(
611
- operative,
612
- ), 'the policy derivation must be able to emit `--merge-spec` (the ' +
613
- 'intakeIntegration-merge path).');
614
- require('derives-propose-spec', /--propose-spec\b/.test(
615
- operative,
616
- ), 'the policy derivation must be able to emit `--propose-spec` (the ' +
617
- 'intakeIntegration-propose path).');
618
- // ORIGIN-TRUST stamp (task untrusted-origin-forces-build-propose): the shell
619
- // must derive `--origin-trust <trusted|untrusted>` from the author-trust case
620
- // (independent of the document mode) and pass it to `intake` so the emitted
621
- // artifact is stamped (the stamp is the SOLE thing author-trust drives).
622
- require('derives-origin-trust-untrusted', /--origin-trust=untrusted\b/.test(
623
- operative,
624
- ), 'the policy derivation must emit `--origin-trust=untrusted` for a non-trusted ' +
625
- 'author (so the emitted artifact is stamped untrusted; task ' +
626
- 'untrusted-origin-forces-build-propose).');
627
- require('derives-origin-trust-trusted', /--origin-trust=trusted\b/.test(
628
- operative,
629
- ), 'the policy derivation must emit `--origin-trust=trusted` for a trusted author.');
630
- require('passes-origin-trust-to-intake', /steps\.policy\.outputs\.origin_trust_flag/.test(
631
- operative,
632
- ), 'the intake invocation must pass the derived `--origin-trust` flag (the ' +
633
- 'stamp must reach `dorfl intake`).');
634
- // The derivation must read the RESOLVED intake DOCUMENT mode via `dorfl config
635
- // --json` (the mechanism `advance` uses), NOT a hardcoded DORFL_* env (ADR
636
- // untrusted-origin-carries-via-stamp-not-forced-staging — the shadowing bug).
637
- require('reads-config-json', /dorfl config --json/.test(
638
- operative,
639
- ), 'the policy derivation must read the resolved config via ' +
640
- '`dorfl config --json` (as `advance` does), so a committed `dorfl.json` ' +
641
- 'mode is honored in CI (not shadowed by a hardcoded env).');
642
- // The DOCUMENT mode is the resolved `intakeIntegration ?? integration` (spec
643
- // intake-integration-knob-and-specs-land-in-proposed-rename): the derivation
644
- // must read `.intakeIntegration` with a `.integration` FALLBACK (the shell twin
645
- // of `intakeIntegration ?? integration`), NOT the autonomy gates. Pin BOTH the
646
- // key and the fallback so a regression to a gate-derived mode is caught.
647
- require('reads-intake-integration', /\.intakeIntegration\b/.test(
648
- operative,
649
- ), 'the policy derivation must read the resolved `intakeIntegration` mode (jq ' +
650
- '`.intakeIntegration` off `dorfl config --json`) for the document mode.');
651
- require('intake-integration-falls-back-to-integration', /\.intakeIntegration\s*\/\/\s*\.integration/.test(
652
- operative,
653
- ), 'the policy derivation must fall back to `.integration` when ' +
654
- '`intakeIntegration` is unset (jq `.intakeIntegration // .integration`, the ' +
655
- 'shell twin of `intakeIntegration ?? integration`).');
656
- // The DOCUMENT mode must NOT be derived from the autonomy gates (spec
657
- // intake-integration-knob-and-specs-land-in-proposed-rename: autoBuild/autoTask
658
- // gate autonomy ONLY, never the document PR-mode). The mode branch must not read
659
- // `.autoBuild` / `.autoTask` off the config json.
660
- require('mode-not-gate-derived', !/\.auto(?:Build|Task)\b/.test(
661
- operative,
662
- ), 'the intake DOCUMENT mode must be derived from `intakeIntegration ?? ' +
663
- 'integration`, NOT the autonomy gates — the derivation must not read ' +
664
- '`.autoBuild` / `.autoTask` (they gate autonomy only; spec ' +
665
- 'intake-integration-knob-and-specs-land-in-proposed-rename).');
666
- // ANTI-REGRESSION (ADR untrusted-origin-carries-via-stamp-not-forced-staging;
667
- // spec US #12): the workflow must NOT emit a `DORFL_AUTO_BUILD:` /
668
- // `DORFL_AUTO_TASK:` env ASSIGNMENT. The env layer OUTRANKS per-repo config, so
669
- // a hardcoded default here SHADOWS the committed `dorfl.json` gates (the bug this
670
- // task fixes). Mirrors `advance-lifecycle-template.ts`'s `no-gate-env-auto-build`
671
- // / `no-gate-env-auto-task`. Checked over the OPERATIVE (non-comment) lines so
672
- // 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.
673
471
  require('no-gate-env-auto-build', !/DORFL_AUTO_BUILD\s*:/.test(
674
472
  operative,
675
473
  ), 'the workflow must NOT emit a `DORFL_AUTO_BUILD:` env assignment (env ' +
676
- 'carries no defaults; the gate is resolved from per-repo config / built-in ' +
677
- 'default — else the env SHADOWS the committed dorfl.json).');
474
+ 'carries no defaults — else the env SHADOWS the committed dorfl.json).');
678
475
  require('no-gate-env-auto-task', !/DORFL_AUTO_TASK\s*:/.test(
679
476
  operative,
680
477
  ), 'the workflow must NOT emit a `DORFL_AUTO_TASK:` env assignment (env ' +
681
- 'carries no defaults; the gate is resolved from per-repo config / built-in ' +
682
- '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).');
683
487
 
684
488
  // --- Insertion point E: the issue-thread review surface ---------------------
685
- // E is REUSED via `intake` (which runs the lone-task review and posts to the
686
- // thread). The workflow must request `issues: write` so that comment can land.
687
489
  require('issues-write-permission', /\bissues:\s*write\b/.test(
688
490
  operative,
689
- ), 'must request `issues: write` so the review verdict / clarifying question ' +
491
+ ), 'must grant `issues: write` so the review verdict / clarifying question ' +
690
492
  'can be posted back into the issue thread (insertion point E).');
691
- // It must NOT route the review verdict through the PR-comment seam — E posts to
692
- // the ISSUE (postIssueComment by number), NOT the PR (postPRComment by url).
693
493
  require('no-pr-comment-seam', !/postPRComment\b/.test(
694
494
  operative,
695
495
  ), 'insertion point E posts to the ISSUE thread (postIssueComment by number), ' +
@@ -717,15 +517,14 @@ export function validateIntakeWorkflow(text: string): IntakeTriggerValidation {
717
517
  text,
718
518
  ), 'the running job must request NO `workflows` permission (US #9: it can ' +
719
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.
720
522
  require('never-edits-dot-github-workflows', !/\.github\/workflows\//.test(
721
- operative,
523
+ operative
524
+ .split('\n')
525
+ .filter((line) => !/^\s*uses: \.\/\.github\/workflows\//.test(line))
526
+ .join('\n'),
722
527
  ), 'no emitted job step may touch `.github/workflows/**` (US #9).');
723
528
 
724
- // --- Wires the SHARED composite setup action -------------------------------
725
- require('uses-shared-setup-action', /uses:\s*\.\/\.github\/actions\/dorfl-setup\b/.test(
726
- text,
727
- ), 'the job must wire the shared composite setup action ' +
728
- '(`./.github/actions/dorfl-setup`, emitted by the core task).');
729
-
730
529
  return {ok: problems.length === 0, problems};
731
530
  }