dorfl 0.1.1 → 0.2.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 (492) hide show
  1. package/dist/advance-ci-template.d.ts +3 -3
  2. package/dist/advance-ci-template.js +1 -1
  3. package/dist/advance-ci-template.js.map +1 -1
  4. package/dist/advance-classify.d.ts +5 -5
  5. package/dist/advance-classify.d.ts.map +1 -1
  6. package/dist/advance-classify.js +4 -4
  7. package/dist/advance-drivers.d.ts +8 -8
  8. package/dist/advance-drivers.d.ts.map +1 -1
  9. package/dist/advance-drivers.js +20 -8
  10. package/dist/advance-drivers.js.map +1 -1
  11. package/dist/advance-isolated.d.ts +3 -3
  12. package/dist/advance-isolated.d.ts.map +1 -1
  13. package/dist/advance-isolated.js +1 -1
  14. package/dist/advance-lifecycle-template.d.ts +2 -2
  15. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  16. package/dist/advance-lifecycle-template.js +85 -13
  17. package/dist/advance-lifecycle-template.js.map +1 -1
  18. package/dist/advance-loop-driver.d.ts +3 -3
  19. package/dist/advance-loop-driver.d.ts.map +1 -1
  20. package/dist/advance-loop-driver.js +1 -1
  21. package/dist/advance-treeless-publish.d.ts +24 -1
  22. package/dist/advance-treeless-publish.d.ts.map +1 -1
  23. package/dist/advance-treeless-publish.js +41 -0
  24. package/dist/advance-treeless-publish.js.map +1 -1
  25. package/dist/advance.d.ts +69 -17
  26. package/dist/advance.d.ts.map +1 -1
  27. package/dist/advance.js +409 -102
  28. package/dist/advance.js.map +1 -1
  29. package/dist/advancing-lock.d.ts +31 -3
  30. package/dist/advancing-lock.d.ts.map +1 -1
  31. package/dist/advancing-lock.js +55 -5
  32. package/dist/advancing-lock.js.map +1 -1
  33. package/dist/agent-launch.d.ts +12 -0
  34. package/dist/agent-launch.d.ts.map +1 -1
  35. package/dist/agent-launch.js +22 -12
  36. package/dist/agent-launch.js.map +1 -1
  37. package/dist/agent-stop.d.ts +40 -2
  38. package/dist/agent-stop.d.ts.map +1 -1
  39. package/dist/agent-stop.js +30 -2
  40. package/dist/agent-stop.js.map +1 -1
  41. package/dist/apply-decide.d.ts +19 -5
  42. package/dist/apply-decide.d.ts.map +1 -1
  43. package/dist/apply-decide.js +38 -9
  44. package/dist/apply-decide.js.map +1 -1
  45. package/dist/apply-merge-action.d.ts +20 -8
  46. package/dist/apply-merge-action.d.ts.map +1 -1
  47. package/dist/apply-merge-action.js +44 -9
  48. package/dist/apply-merge-action.js.map +1 -1
  49. package/dist/apply-persist.d.ts +62 -31
  50. package/dist/apply-persist.d.ts.map +1 -1
  51. package/dist/apply-persist.js +173 -44
  52. package/dist/apply-persist.js.map +1 -1
  53. package/dist/apply-stuck-action.d.ts +151 -0
  54. package/dist/apply-stuck-action.d.ts.map +1 -0
  55. package/dist/apply-stuck-action.js +125 -0
  56. package/dist/apply-stuck-action.js.map +1 -0
  57. package/dist/brand.d.ts +12 -1
  58. package/dist/brand.d.ts.map +1 -1
  59. package/dist/brand.js +2 -1
  60. package/dist/brand.js.map +1 -1
  61. package/dist/buildable-body.d.ts +14 -14
  62. package/dist/buildable-body.js +9 -9
  63. package/dist/claim-cas.d.ts +3 -3
  64. package/dist/claim-cas.js +1 -1
  65. package/dist/claim-cas.js.map +1 -1
  66. package/dist/cli-spinner.d.ts +1 -1
  67. package/dist/cli-spinner.js +1 -1
  68. package/dist/cli.d.ts +10 -0
  69. package/dist/cli.d.ts.map +1 -1
  70. package/dist/cli.js +385 -128
  71. package/dist/cli.js.map +1 -1
  72. package/dist/close-job-template.d.ts +3 -3
  73. package/dist/close-job-template.js +9 -9
  74. package/dist/close-job.d.ts +8 -8
  75. package/dist/close-job.js +25 -25
  76. package/dist/close-job.js.map +1 -1
  77. package/dist/complete.d.ts +9 -6
  78. package/dist/complete.d.ts.map +1 -1
  79. package/dist/complete.js +99 -45
  80. package/dist/complete.js.map +1 -1
  81. package/dist/concurrency.d.ts +1 -1
  82. package/dist/concurrency.js +1 -1
  83. package/dist/config.d.ts +86 -40
  84. package/dist/config.d.ts.map +1 -1
  85. package/dist/config.js +61 -9
  86. package/dist/config.js.map +1 -1
  87. package/dist/continue-branch.d.ts +1 -1
  88. package/dist/continue-branch.d.ts.map +1 -1
  89. package/dist/continue-branch.js +23 -1
  90. package/dist/continue-branch.js.map +1 -1
  91. package/dist/cwd-section.js +1 -1
  92. package/dist/cwd-section.js.map +1 -1
  93. package/dist/decision-engine.d.ts +39 -11
  94. package/dist/decision-engine.d.ts.map +1 -1
  95. package/dist/decision-engine.js +10 -6
  96. package/dist/decision-engine.js.map +1 -1
  97. package/dist/do-autopick.d.ts +6 -6
  98. package/dist/do-autopick.d.ts.map +1 -1
  99. package/dist/do-autopick.js +16 -6
  100. package/dist/do-autopick.js.map +1 -1
  101. package/dist/do-config.d.ts +4 -4
  102. package/dist/do-config.js +3 -3
  103. package/dist/do-config.js.map +1 -1
  104. package/dist/do-remote-auto.d.ts +2 -2
  105. package/dist/do-remote-auto.js +1 -1
  106. package/dist/do.d.ts +74 -81
  107. package/dist/do.d.ts.map +1 -1
  108. package/dist/do.js +439 -50
  109. package/dist/do.js.map +1 -1
  110. package/dist/drop-source.d.ts +2 -2
  111. package/dist/env-config.d.ts.map +1 -1
  112. package/dist/env-config.js +19 -11
  113. package/dist/env-config.js.map +1 -1
  114. package/dist/failure-cause.d.ts +3 -2
  115. package/dist/failure-cause.d.ts.map +1 -1
  116. package/dist/failure-cause.js +28 -1
  117. package/dist/failure-cause.js.map +1 -1
  118. package/dist/format.d.ts +6 -6
  119. package/dist/format.d.ts.map +1 -1
  120. package/dist/format.js +13 -30
  121. package/dist/format.js.map +1 -1
  122. package/dist/frontmatter.d.ts +28 -15
  123. package/dist/frontmatter.d.ts.map +1 -1
  124. package/dist/frontmatter.js +38 -24
  125. package/dist/frontmatter.js.map +1 -1
  126. package/dist/gc.d.ts +65 -6
  127. package/dist/gc.d.ts.map +1 -1
  128. package/dist/gc.js +126 -9
  129. package/dist/gc.js.map +1 -1
  130. package/dist/github.d.ts +14 -0
  131. package/dist/github.d.ts.map +1 -1
  132. package/dist/github.js +73 -0
  133. package/dist/github.js.map +1 -1
  134. package/dist/harness.d.ts +25 -0
  135. package/dist/harness.d.ts.map +1 -1
  136. package/dist/harness.js.map +1 -1
  137. package/dist/index.d.ts +4 -1
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +2 -1
  140. package/dist/index.js.map +1 -1
  141. package/dist/install-ci-branch-protection.d.ts +133 -39
  142. package/dist/install-ci-branch-protection.d.ts.map +1 -1
  143. package/dist/install-ci-branch-protection.js +191 -49
  144. package/dist/install-ci-branch-protection.js.map +1 -1
  145. package/dist/install-ci-capabilities/advance-lifecycle.d.ts +1 -1
  146. package/dist/install-ci-capabilities/advance-lifecycle.js +1 -1
  147. package/dist/install-ci-capabilities/close-job.d.ts +1 -1
  148. package/dist/install-ci-capabilities/close-job.js +1 -1
  149. package/dist/install-ci-capabilities/example-noop.d.ts +1 -1
  150. package/dist/install-ci-capabilities/example-noop.js +1 -1
  151. package/dist/install-ci-capabilities/intake.d.ts +2 -2
  152. package/dist/install-ci-capabilities/intake.js +2 -2
  153. package/dist/install-ci-capabilities/verify.d.ts +1 -1
  154. package/dist/install-ci-capabilities/verify.js +1 -1
  155. package/dist/install-ci-core.d.ts +24 -4
  156. package/dist/install-ci-core.d.ts.map +1 -1
  157. package/dist/install-ci-core.js +12 -5
  158. package/dist/install-ci-core.js.map +1 -1
  159. package/dist/install-ci-github.d.ts +33 -1
  160. package/dist/install-ci-github.d.ts.map +1 -1
  161. package/dist/install-ci-github.js +74 -1
  162. package/dist/install-ci-github.js.map +1 -1
  163. package/dist/install-ci.d.ts +1 -1
  164. package/dist/install-ci.js +4 -4
  165. package/dist/install-ci.js.map +1 -1
  166. package/dist/install-skills.d.ts +123 -0
  167. package/dist/install-skills.d.ts.map +1 -0
  168. package/dist/install-skills.js +100 -0
  169. package/dist/install-skills.js.map +1 -0
  170. package/dist/intake-event.d.ts +6 -6
  171. package/dist/intake-event.js +6 -6
  172. package/dist/intake-marker.d.ts +3 -3
  173. package/dist/intake-marker.d.ts.map +1 -1
  174. package/dist/intake-triage.d.ts +1 -1
  175. package/dist/intake-triage.js +2 -2
  176. package/dist/intake-trigger-template.d.ts +8 -8
  177. package/dist/intake-trigger-template.d.ts.map +1 -1
  178. package/dist/intake-trigger-template.js +17 -17
  179. package/dist/intake-trigger-template.js.map +1 -1
  180. package/dist/intake.d.ts +65 -52
  181. package/dist/intake.d.ts.map +1 -1
  182. package/dist/intake.js +92 -76
  183. package/dist/intake.js.map +1 -1
  184. package/dist/integration-core.d.ts +27 -24
  185. package/dist/integration-core.d.ts.map +1 -1
  186. package/dist/integration-core.js +191 -77
  187. package/dist/integration-core.js.map +1 -1
  188. package/dist/integrator.d.ts +1 -1
  189. package/dist/integrator.d.ts.map +1 -1
  190. package/dist/integrator.js +19 -3
  191. package/dist/integrator.js.map +1 -1
  192. package/dist/isolation.d.ts +4 -4
  193. package/dist/isolation.d.ts.map +1 -1
  194. package/dist/isolation.js +9 -0
  195. package/dist/isolation.js.map +1 -1
  196. package/dist/issue-provider.d.ts +4 -4
  197. package/dist/issue-provider.js +1 -1
  198. package/dist/item-lock.d.ts +216 -149
  199. package/dist/item-lock.d.ts.map +1 -1
  200. package/dist/item-lock.js +345 -270
  201. package/dist/item-lock.js.map +1 -1
  202. package/dist/item-path.d.ts +2 -2
  203. package/dist/item-path.js +2 -2
  204. package/dist/ledger-lint.d.ts +9 -9
  205. package/dist/ledger-lint.js +9 -9
  206. package/dist/ledger-read.d.ts +57 -57
  207. package/dist/ledger-read.d.ts.map +1 -1
  208. package/dist/ledger-read.js +24 -24
  209. package/dist/ledger-read.js.map +1 -1
  210. package/dist/ledger-write.d.ts +33 -28
  211. package/dist/ledger-write.d.ts.map +1 -1
  212. package/dist/ledger-write.js +100 -122
  213. package/dist/ledger-write.js.map +1 -1
  214. package/dist/lifecycle-gather.d.ts +18 -1
  215. package/dist/lifecycle-gather.d.ts.map +1 -1
  216. package/dist/lifecycle-gather.js +23 -17
  217. package/dist/lifecycle-gather.js.map +1 -1
  218. package/dist/lifecycle-pools.d.ts +44 -7
  219. package/dist/lifecycle-pools.d.ts.map +1 -1
  220. package/dist/lifecycle-pools.js +27 -7
  221. package/dist/lifecycle-pools.js.map +1 -1
  222. package/dist/merge-question-surfacer.d.ts +18 -4
  223. package/dist/merge-question-surfacer.d.ts.map +1 -1
  224. package/dist/merge-question-surfacer.js +20 -5
  225. package/dist/merge-question-surfacer.js.map +1 -1
  226. package/dist/migrate-stuck-locks.d.ts +129 -0
  227. package/dist/migrate-stuck-locks.d.ts.map +1 -0
  228. package/dist/migrate-stuck-locks.js +355 -0
  229. package/dist/migrate-stuck-locks.js.map +1 -0
  230. package/dist/mint-adr.js +4 -4
  231. package/dist/mirror-pool-scan.d.ts +2 -2
  232. package/dist/mirror-pool-scan.js +3 -3
  233. package/dist/mirror-pool-scan.js.map +1 -1
  234. package/dist/needs-attention.d.ts +304 -27
  235. package/dist/needs-attention.d.ts.map +1 -1
  236. package/dist/needs-attention.js +542 -67
  237. package/dist/needs-attention.js.map +1 -1
  238. package/dist/orphan-sidecar.d.ts +10 -6
  239. package/dist/orphan-sidecar.d.ts.map +1 -1
  240. package/dist/orphan-sidecar.js +35 -2
  241. package/dist/orphan-sidecar.js.map +1 -1
  242. package/dist/pi-harness.d.ts +16 -0
  243. package/dist/pi-harness.d.ts.map +1 -1
  244. package/dist/pi-harness.js +82 -2
  245. package/dist/pi-harness.js.map +1 -1
  246. package/dist/placement.d.ts +8 -8
  247. package/dist/placement.js +3 -3
  248. package/dist/prd-to-spec.d.ts.map +1 -1
  249. package/dist/prd-to-spec.js +9 -5
  250. package/dist/prd-to-spec.js.map +1 -1
  251. package/dist/prompt.d.ts +16 -19
  252. package/dist/prompt.d.ts.map +1 -1
  253. package/dist/prompt.js +17 -19
  254. package/dist/prompt.js.map +1 -1
  255. package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
  256. package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
  257. package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
  258. package/dist/protocol/TASKING-PROTOCOL.md +3 -1
  259. package/dist/protocol/WORK-CONTRACT.md +22 -18
  260. package/dist/protocol/task-template.md +1 -1
  261. package/dist/readiness.d.ts +1 -1
  262. package/dist/reap-branches.d.ts +12 -9
  263. package/dist/reap-branches.d.ts.map +1 -1
  264. package/dist/reap-branches.js +25 -7
  265. package/dist/reap-branches.js.map +1 -1
  266. package/dist/recover-isolated.d.ts +12 -0
  267. package/dist/recover-isolated.d.ts.map +1 -1
  268. package/dist/recover-isolated.js +6 -1
  269. package/dist/recover-isolated.js.map +1 -1
  270. package/dist/repo-config.d.ts +23 -2
  271. package/dist/repo-config.d.ts.map +1 -1
  272. package/dist/repo-config.js +64 -18
  273. package/dist/repo-config.js.map +1 -1
  274. package/dist/repo-mirror.d.ts.map +1 -1
  275. package/dist/repo-mirror.js +18 -2
  276. package/dist/repo-mirror.js.map +1 -1
  277. package/dist/review-gate.d.ts +3 -3
  278. package/dist/review-gate.d.ts.map +1 -1
  279. package/dist/review-gate.js +11 -10
  280. package/dist/review-gate.js.map +1 -1
  281. package/dist/review-verdict.d.ts +2 -2
  282. package/dist/review-verdict.d.ts.map +1 -1
  283. package/dist/review-verdict.js +2 -2
  284. package/dist/review-verdict.js.map +1 -1
  285. package/dist/run.d.ts +2 -2
  286. package/dist/run.d.ts.map +1 -1
  287. package/dist/run.js +58 -23
  288. package/dist/run.js.map +1 -1
  289. package/dist/scan.d.ts +26 -17
  290. package/dist/scan.d.ts.map +1 -1
  291. package/dist/scan.js +29 -15
  292. package/dist/scan.js.map +1 -1
  293. package/dist/select-order.d.ts +1 -1
  294. package/dist/select-priority.d.ts +14 -14
  295. package/dist/select-priority.d.ts.map +1 -1
  296. package/dist/select-priority.js +5 -5
  297. package/dist/select-priority.js.map +1 -1
  298. package/dist/sidecar-apply.js +1 -1
  299. package/dist/sidecar-apply.js.map +1 -1
  300. package/dist/sidecar.d.ts +47 -12
  301. package/dist/sidecar.d.ts.map +1 -1
  302. package/dist/sidecar.js +84 -4
  303. package/dist/sidecar.js.map +1 -1
  304. package/dist/skills/answer-questions/SKILL.md +89 -0
  305. package/dist/skills/capture-signal/SKILL.md +52 -0
  306. package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
  307. package/dist/skills/drive-tasks/SKILL.md +218 -0
  308. package/dist/skills/from-idea/SKILL.md +83 -0
  309. package/dist/skills/merge-prs/SKILL.md +70 -0
  310. package/dist/skills/orchestrate/SKILL.md +101 -0
  311. package/dist/skills/promote/SKILL.md +35 -0
  312. package/dist/skills/review/SKILL.md +16 -0
  313. package/dist/skills/setup/SKILL.md +258 -0
  314. package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
  315. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
  316. package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
  317. package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
  318. package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
  319. package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
  320. package/dist/skills/setup/protocol/spec-template.md +71 -0
  321. package/dist/skills/setup/protocol/task-template.md +65 -0
  322. package/dist/skills/surface-questions/SKILL.md +16 -0
  323. package/dist/skills/to-spec/SKILL.md +34 -0
  324. package/dist/skills/to-task/SKILL.md +19 -0
  325. package/dist/skills/triage-observations/SKILL.md +78 -0
  326. package/dist/skills/work/SKILL.md +51 -0
  327. package/dist/slug-namespace.d.ts +6 -6
  328. package/dist/slug-namespace.js +8 -8
  329. package/dist/slug-namespace.js.map +1 -1
  330. package/dist/spec-complete.d.ts.map +1 -1
  331. package/dist/spec-complete.js +4 -5
  332. package/dist/spec-complete.js.map +1 -1
  333. package/dist/start.d.ts +1 -1
  334. package/dist/start.d.ts.map +1 -1
  335. package/dist/start.js +54 -60
  336. package/dist/start.js.map +1 -1
  337. package/dist/status.d.ts +3 -3
  338. package/dist/status.js +4 -4
  339. package/dist/status.js.map +1 -1
  340. package/dist/surface-gate.d.ts +2 -2
  341. package/dist/surface-gate.d.ts.map +1 -1
  342. package/dist/surface-gate.js +10 -3
  343. package/dist/surface-gate.js.map +1 -1
  344. package/dist/surface-persist.d.ts +2 -2
  345. package/dist/surface-persist.d.ts.map +1 -1
  346. package/dist/surface-persist.js +1 -1
  347. package/dist/surface-persist.js.map +1 -1
  348. package/dist/tasker-review-loop.d.ts +11 -10
  349. package/dist/tasker-review-loop.d.ts.map +1 -1
  350. package/dist/tasker-review-loop.js +8 -8
  351. package/dist/tasker-review-loop.js.map +1 -1
  352. package/dist/tasking-eligibility.d.ts +21 -21
  353. package/dist/tasking-eligibility.d.ts.map +1 -1
  354. package/dist/tasking-eligibility.js +10 -10
  355. package/dist/tasking-lock.d.ts +10 -10
  356. package/dist/tasking-lock.d.ts.map +1 -1
  357. package/dist/tasking-lock.js +69 -49
  358. package/dist/tasking-lock.js.map +1 -1
  359. package/dist/tasking.d.ts +73 -39
  360. package/dist/tasking.d.ts.map +1 -1
  361. package/dist/tasking.js +309 -95
  362. package/dist/tasking.js.map +1 -1
  363. package/dist/triage-gate.d.ts +1 -1
  364. package/dist/triage-persist.d.ts +15 -11
  365. package/dist/triage-persist.d.ts.map +1 -1
  366. package/dist/triage-persist.js +48 -18
  367. package/dist/triage-persist.js.map +1 -1
  368. package/dist/vendor/incur/agents.d.ts +58 -0
  369. package/dist/vendor/incur/agents.d.ts.map +1 -0
  370. package/dist/vendor/incur/agents.js +343 -0
  371. package/dist/vendor/incur/agents.js.map +1 -0
  372. package/dist/verify-workflow-template.d.ts +1 -1
  373. package/dist/verify-workflow-template.js +3 -3
  374. package/dist/watch-session.d.ts +11 -3
  375. package/dist/watch-session.d.ts.map +1 -1
  376. package/dist/watch-session.js +94 -7
  377. package/dist/watch-session.js.map +1 -1
  378. package/dist/work-layout.d.ts +15 -9
  379. package/dist/work-layout.d.ts.map +1 -1
  380. package/dist/work-layout.js +14 -9
  381. package/dist/work-layout.js.map +1 -1
  382. package/dist/workspace.d.ts +2 -2
  383. package/package.json +2 -2
  384. package/src/advance-ci-template.ts +4 -4
  385. package/src/advance-classify.ts +5 -5
  386. package/src/advance-drivers.ts +26 -13
  387. package/src/advance-isolated.ts +3 -3
  388. package/src/advance-lifecycle-template.ts +98 -13
  389. package/src/advance-loop-driver.ts +3 -3
  390. package/src/advance-treeless-publish.ts +46 -1
  391. package/src/advance.ts +495 -115
  392. package/src/advancing-lock.ts +102 -7
  393. package/src/agent-launch.ts +37 -12
  394. package/src/agent-stop.ts +60 -2
  395. package/src/apply-decide.ts +38 -9
  396. package/src/apply-merge-action.ts +49 -11
  397. package/src/apply-persist.ts +236 -62
  398. package/src/apply-stuck-action.ts +260 -0
  399. package/src/brand.ts +14 -2
  400. package/src/buildable-body.ts +14 -14
  401. package/src/claim-cas.ts +4 -4
  402. package/src/cli-spinner.ts +1 -1
  403. package/src/cli.ts +472 -141
  404. package/src/close-job-template.ts +9 -9
  405. package/src/close-job.ts +26 -26
  406. package/src/complete.ts +121 -63
  407. package/src/concurrency.ts +1 -1
  408. package/src/config.ts +143 -49
  409. package/src/continue-branch.ts +23 -1
  410. package/src/cwd-section.ts +1 -1
  411. package/src/decision-engine.ts +57 -19
  412. package/src/do-autopick.ts +21 -10
  413. package/src/do-config.ts +7 -7
  414. package/src/do-remote-auto.ts +2 -2
  415. package/src/do.ts +558 -85
  416. package/src/drop-source.ts +2 -2
  417. package/src/env-config.ts +19 -11
  418. package/src/failure-cause.ts +30 -1
  419. package/src/format.ts +13 -33
  420. package/src/frontmatter.ts +55 -35
  421. package/src/gc.ts +172 -9
  422. package/src/github.ts +78 -0
  423. package/src/harness.ts +25 -0
  424. package/src/index.ts +10 -0
  425. package/src/install-ci-branch-protection.ts +283 -58
  426. package/src/install-ci-capabilities/advance-lifecycle.ts +1 -1
  427. package/src/install-ci-capabilities/close-job.ts +1 -1
  428. package/src/install-ci-capabilities/example-noop.ts +1 -1
  429. package/src/install-ci-capabilities/intake.ts +2 -2
  430. package/src/install-ci-capabilities/verify.ts +1 -1
  431. package/src/install-ci-core.ts +34 -7
  432. package/src/install-ci-github.ts +87 -1
  433. package/src/install-ci.ts +4 -4
  434. package/src/install-skills.ts +166 -0
  435. package/src/intake-event.ts +6 -6
  436. package/src/intake-marker.ts +3 -3
  437. package/src/intake-triage.ts +2 -2
  438. package/src/intake-trigger-template.ts +18 -18
  439. package/src/intake.ts +138 -103
  440. package/src/integration-core.ts +234 -100
  441. package/src/integrator.ts +22 -9
  442. package/src/isolation.ts +13 -4
  443. package/src/issue-provider.ts +4 -4
  444. package/src/item-lock.ts +505 -361
  445. package/src/item-path.ts +2 -2
  446. package/src/ledger-lint.ts +9 -9
  447. package/src/ledger-read.ts +81 -81
  448. package/src/ledger-write.ts +137 -152
  449. package/src/lifecycle-gather.ts +45 -22
  450. package/src/lifecycle-pools.ts +71 -13
  451. package/src/merge-question-surfacer.ts +32 -8
  452. package/src/migrate-stuck-locks.ts +451 -0
  453. package/src/mint-adr.ts +4 -4
  454. package/src/mirror-pool-scan.ts +5 -5
  455. package/src/needs-attention.ts +877 -104
  456. package/src/orphan-sidecar.ts +49 -8
  457. package/src/pi-harness.ts +82 -2
  458. package/src/placement.ts +8 -8
  459. package/src/prd-to-spec.ts +13 -5
  460. package/src/prompt.ts +20 -25
  461. package/src/readiness.ts +1 -1
  462. package/src/reap-branches.ts +38 -14
  463. package/src/recover-isolated.ts +18 -1
  464. package/src/repo-config.ts +66 -17
  465. package/src/repo-mirror.ts +23 -1
  466. package/src/review-gate.ts +12 -11
  467. package/src/review-verdict.ts +3 -3
  468. package/src/run.ts +69 -23
  469. package/src/scan.ts +36 -22
  470. package/src/select-order.ts +1 -1
  471. package/src/select-priority.ts +15 -15
  472. package/src/sidecar-apply.ts +1 -1
  473. package/src/sidecar.ts +141 -14
  474. package/src/slug-namespace.ts +10 -10
  475. package/src/spec-complete.ts +4 -5
  476. package/src/start.ts +57 -63
  477. package/src/status.ts +7 -7
  478. package/src/surface-gate.ts +12 -5
  479. package/src/surface-persist.ts +3 -3
  480. package/src/tasker-review-loop.ts +18 -17
  481. package/src/tasking-eligibility.ts +21 -21
  482. package/src/tasking-lock.ts +75 -60
  483. package/src/tasking.ts +381 -135
  484. package/src/triage-gate.ts +1 -1
  485. package/src/triage-persist.ts +71 -27
  486. package/src/vendor/incur/LICENSE +21 -0
  487. package/src/vendor/incur/README.md +19 -0
  488. package/src/vendor/incur/agents.ts +392 -0
  489. package/src/verify-workflow-template.ts +3 -3
  490. package/src/watch-session.ts +102 -7
  491. package/src/work-layout.ts +14 -9
  492. package/src/workspace.ts +2 -2
package/src/scan.ts CHANGED
@@ -53,7 +53,7 @@ export function lifecycleGatesFrom(config: {
53
53
  surfaceBlockers: boolean;
54
54
  /**
55
55
  * Spec `staging-surface-and-apply-promote-safety` F2 — the gate that widens
56
- * the SURFACE candidate set into STAGING (`tasks/backlog/` + `prds/proposed/`).
56
+ * the SURFACE candidate set into STAGING (`tasks/backlog/` + `specs/proposed/`).
57
57
  * Threaded here so the `scan --json` `lifecycle.surface[]` reflects the
58
58
  * expanded pool and the CI matrix enumerates staging surface legs. BUILD/claim
59
59
  * stays pool-only — only this lifecycle path widens.
@@ -78,7 +78,7 @@ export interface ScannedTriageItem {
78
78
  }
79
79
  /**
80
80
  * A surface/apply lifecycle item, with its namespace. SURFACE only ever carries
81
- * `needsAnswers` task/prd items (an observation with no sidecar goes to `triage`,
81
+ * `needsAnswers` task/spec items (an observation with no sidecar goes to `triage`,
82
82
  * not `surface`). APPLY, however, ALSO carries an ANSWERED OBSERVATION: since the
83
83
  * classifier (`buildLifecyclePools`) routes an observation whose sidecar is
84
84
  * all-answered to `apply` (CONSUME, always-on), the projection MUST keep it here
@@ -108,7 +108,7 @@ export interface ScannedBlockedItem {
108
108
  export interface ScannedLifecycle {
109
109
  /** Untriaged observations → `obs:<slug>` legs (gated by `observationTriage`). */
110
110
  triage: ScannedTriageItem[];
111
- /** `needsAnswers` task/prd items with no answered sidecar → surface legs (gated by `surfaceBlockers`). */
111
+ /** `needsAnswers` task/spec items with no answered sidecar → surface legs (gated by `surfaceBlockers`). */
112
112
  surface: ScannedBlockedItem[];
113
113
  /**
114
114
  * Items WITH an all-answered sidecar → apply legs (CONSUME, always-on). Carries
@@ -132,7 +132,7 @@ export interface ScannedItem extends ReadyItem {
132
132
  }
133
133
 
134
134
  /**
135
- * A PRD entry in `scan --json`'s taskable-prd pool — the SAME shape an eligible
135
+ * A SPEC entry in `scan --json`'s taskable-spec pool — the SAME shape an eligible
136
136
  * task carries in `items[]` (a `slug` + an `eligibility.eligible` boolean), so
137
137
  * the propose-matrix `jq` filter mirrors the task one: `select(.eligibility.eligible)
138
138
  * | "spec:" + .slug`. "Eligible" here means TASKABLE — the per-repo `autoTask`
@@ -159,7 +159,7 @@ export interface RepoReport {
159
159
  items: ScannedItem[];
160
160
  /**
161
161
  * The TASKABLE-SPEC pool for this repo (the `specs[]` companion of `items[]`):
162
- * every spec in `work/prds/ready/` not already in `work/prds/tasked/`, each tagged with
162
+ * every spec in `work/specs/ready/` not already in `work/specs/tasked/`, each tagged with
163
163
  * `eligibility.eligible` from {@link taskableSpecs} (the SAME `autoslice-gate`
164
164
  * predicate the mirror-side pool scan uses — NOT a forked predicate). This is
165
165
  * what makes the propose-mode CI matrix enumerate `spec:<slug>` legs for ready
@@ -183,7 +183,7 @@ export interface RepoReport {
183
183
  */
184
184
  lifecycle: ScannedLifecycle;
185
185
  /**
186
- * The one-slug-one-folder LINT result (prd `ledger-integrity` story 3): any
186
+ * The one-slug-one-folder LINT result (spec `ledger-integrity` story 3): any
187
187
  * slug present in MORE THAN ONE `work/` status folder in THIS repo's ledger.
188
188
  * Empty ⇒ a clean ledger. Non-empty ⇒ a corrupt ledger the formatter WARNS
189
189
  * about loudly and a human must resolve (never auto-fixed). Derived by listing
@@ -191,7 +191,7 @@ export interface RepoReport {
191
191
  */
192
192
  ledgerDuplicates: DuplicateSlug[];
193
193
  /**
194
- * The PER-ITEM LOCK in-flight view for this repo (prd
194
+ * The PER-ITEM LOCK in-flight view for this repo (spec
195
195
  * `ledger-status-per-item-lock-refs` US #8; task
196
196
  * `needs-attention-as-stuck-lock-state`): the held lock entries read from the
197
197
  * repo's `refs/dorfl/lock/*` refs — `active` holds (in-progress) and
@@ -240,7 +240,7 @@ export function readReadyItems(repoPath: string): ReadyItem[] {
240
240
  * through the EXACT same eligibility path as in-place (`do-autopick`), not a fork.
241
241
  */
242
242
  /**
243
- * Score a prd pool down to its TASKABLE subset, then label every prd with
243
+ * Score a spec pool down to its TASKABLE subset, then label every spec with
244
244
  * `eligibility.eligible` (true ⇔ taskable). REUSES {@link taskableSpecs} —
245
245
  * the SAME `autoslice-gate` predicate the mirror-side `scanMirrorPool` + the
246
246
  * in-place `do-autopick` pool already run — so what is taskable does not
@@ -280,8 +280,8 @@ export function scoreSpecs(
280
280
  * re-enumeration — a pure shape map over the already-gated pools.
281
281
  *
282
282
  * The pool items' `namespace` is the wider {@link SelectedNamespace}. SURFACE by
283
- * construction only carries `'task'`/`'prd'` (an observation with no sidecar is a
284
- * `triage` candidate, never `surface`), so it narrows + drops any non-task/prd
283
+ * construction only carries `'task'`/`'spec'` (an observation with no sidecar is a
284
+ * `triage` candidate, never `surface`), so it narrows + drops any non-task/spec
285
285
  * defensively. APPLY additionally admits `'observation'` (an answered observation
286
286
  * sidecar → apply), and it MUST be kept so the matrix `jq` emits its
287
287
  * `observation:<slug>` apply leg — narrowing it away here is exactly what stranded
@@ -324,7 +324,7 @@ export function scoreItems(
324
324
  counts: {totalItems: number; totalEligible: number},
325
325
  heldSlugs: Set<string> = new Set(),
326
326
  ): ScannedItem[] {
327
- // HELD-SLUG SUBTRACTION (prd `ledger-status-per-item-lock-refs` US #15; task
327
+ // HELD-SLUG SUBTRACTION (spec `ledger-status-per-item-lock-refs` US #15; task
328
328
  // `claim-acquires-unified-lock-no-body-move`): exclude any pool slug whose
329
329
  // per-item lock is currently held — the eligible pool is "in `tasks/ready/` on
330
330
  // `main` AND no lock held".
@@ -435,7 +435,7 @@ export async function scan(
435
435
  // the lock refs from the mirror's origin; non-fatal (empty set on any fault),
436
436
  // so the read-only scan degrades gracefully exactly as its config reads do.
437
437
  const heldSlugs = await heldTaskSlugs(mirror.path, 'origin', options.env);
438
- // The PER-ITEM LOCK in-flight view (prd US #8; task
438
+ // The PER-ITEM LOCK in-flight view (spec US #8; task
439
439
  // `needs-attention-as-stuck-lock-state`): ADDITIONALLY read the full held
440
440
  // lock entries (action × state + reason) from the mirror's lock refs, so the
441
441
  // scan surfaces held (in-progress) + stuck (needs-attention) items. Read from
@@ -448,7 +448,7 @@ export async function scan(
448
448
  'origin',
449
449
  options.env,
450
450
  );
451
- // Spec pool — the TASKABLE-PRD companion of the task pool above
451
+ // Spec pool — the TASKABLE-SPEC companion of the task pool above
452
452
  // (`ci-propose-matrix-must-enumerate-sliceable-prds-not-only-slices`). Resolve
453
453
  // `autoTask` PER REPO from the mirror's COMMITTED `.dorfl.json`
454
454
  // (exactly as the mirror-side pool scan does — NOT forked); a read fault is
@@ -508,7 +508,7 @@ export async function scan(
508
508
  env: options.env,
509
509
  }),
510
510
  );
511
- // The one-slug-one-folder LINT (prd story 3): derive any slug residing in >1
511
+ // The one-slug-one-folder LINT (spec story 3): derive any slug residing in >1
512
512
  // status folder from the mirror's committed `main` tree (the SAME `ls-tree`
513
513
  // read the seam uses), so a corrupt ledger is surfaced LOUDLY by the formatter.
514
514
  const ledgerDuplicates = lintRefLedger('main', mirror.path, options.env);
@@ -543,13 +543,22 @@ export function scanRepoPaths(
543
543
  repoPaths: string[],
544
544
  config: Config,
545
545
  /**
546
- * The HELD-SLUG set to SUBTRACT from each repo's pool (`tasks/ready/`) (prd
547
- * `ledger-status-per-item-lock-refs` US #15). This is a WORKING-TREE, OFFLINE
548
- * scan (it has no arbiter handle to fetch the lock refs from that is the
549
- * registry `scan`'s job), so the held set is supplied by the in-place CALLER
550
- * (which knows its arbiter) and DEFAULTS to empty: with the body still moving to
551
- * `in-progress/` on claim the subtraction is redundant-but-harmless, so omitting
552
- * it preserves the offline read while keeping the seam in place for task #9.
546
+ * The HELD-SLUG set to SUBTRACT from each repo's pool (`tasks/ready/`) AND from
547
+ * the in-place lifecycle triage/surface/apply pools (spec
548
+ * `ledger-status-per-item-lock-refs` US #15; task
549
+ * `in-place-scan-subtracts-held-locked-slugs-from-propose-matrix`). Passed
550
+ * BOTH to {@link scoreItems} AND to {@link gatherLifecycleInPlace}, so a slug
551
+ * whose per-item lock is currently held (state `active`/`stuck`) never leaks
552
+ * into the propose matrix via EITHER the eligible-task pool OR a lifecycle
553
+ * leg — CI would otherwise enumerate a `task:<slug>` surface/apply leg that
554
+ * always loses the claim CAS.
555
+ *
556
+ * This is a WORKING-TREE, OFFLINE scan (it has no arbiter handle to fetch the
557
+ * lock refs from — that is the registry `scan`'s job), so the held set is
558
+ * supplied by the in-place CALLER (which knows its arbiter, e.g.
559
+ * `resolveCwdSection` reads it fail-CLOSED via `heldTaskSlugsStrict`) and
560
+ * DEFAULTS to empty. An empty default preserves the offline read while keeping
561
+ * the seam in place.
553
562
  */
554
563
  heldSlugs: Set<string> = new Set(),
555
564
  /**
@@ -569,7 +578,7 @@ export function scanRepoPaths(
569
578
  global: config,
570
579
  override,
571
580
  }).config;
572
- // Spec pool — the TASKABLE-PRD companion of the task pool. Resolve
581
+ // Spec pool — the TASKABLE-SPEC companion of the task pool. Resolve
573
582
  // `autoTask` PER REPO from the working-tree `.dorfl.json` (the same
574
583
  // way `autoBuild` is resolved); `taskableSpecs` (the SAME `autoslice-gate`
575
584
  // predicate the autopick paths run) decides what is taskable — no forked
@@ -590,6 +599,11 @@ export function scanRepoPaths(
590
599
  surfaceBlockers: resolved.surfaceBlockers,
591
600
  surfaceStaging: resolved.surfaceStaging,
592
601
  }),
602
+ // Held-slug subtraction on the LIFECYCLE side (task
603
+ // `in-place-scan-subtracts-held-locked-slugs-from-propose-matrix`):
604
+ // symmetric to the `scoreItems` subtraction above, so a held task never
605
+ // leaks into a surface/apply leg either.
606
+ heldSlugs,
593
607
  }),
594
608
  );
595
609
  // The one-slug-one-folder LINT over THIS working tree's `work/` ledger.
@@ -28,7 +28,7 @@
28
28
  * member of this union and naming it is a usage error.
29
29
  *
30
30
  * - `build` — build an eligible task;
31
- * - `task` — task a taskable prd;
31
+ * - `task` — task a taskable spec;
32
32
  * - `surface` — render a `needsAnswers` blocker into an answerable sidecar;
33
33
  * - `triage` — triage an untriaged observation.
34
34
  */
@@ -30,16 +30,16 @@ import {
30
30
  * land). Build it standalone; do not assume `run` already calls it.
31
31
  *
32
32
  * **Up to FIVE pools.** The `scan`/`selectCandidates`/eligibility model is
33
- * TASK-ONLY (there is no prd candidate). So this helper composes:
33
+ * TASK-ONLY (there is no spec candidate). So this helper composes:
34
34
  *
35
35
  * - the **`build` pool** (eligible TASKS) — the EXISTING {@link selectCandidates}
36
36
  * path (round-robin across repos, capped). This is the EXACT task-selection
37
37
  * primitive `run` uses, so `run` and this helper SHARE it (they share
38
38
  * `selectCandidates`, the task-pool core).
39
- * - the **`task` pool** (prd-to-task) — a pool the caller builds from the prd reader
39
+ * - the **`task` pool** (spec-to-task) — a pool the caller builds from the spec reader
40
40
  * (`ledgerRead.resolveSpecPool`) filtered by `autoslice-gate`'s pure predicate
41
41
  * ({@link resolveTaskingEligibility}); see {@link taskableSpecs}. The helper
42
- * does NOT reinvent prd eligibility.
42
+ * does NOT reinvent spec eligibility.
43
43
  *
44
44
  * `do` is STRICTLY SEQUENTIAL (parallelism is `run`'s job, ADR §3) — this helper
45
45
  * only ORDERS + COUNTS the items; the caller runs the existing `do` pipeline per
@@ -49,7 +49,7 @@ import {
49
49
 
50
50
  /**
51
51
  * Which namespace a selected item names (mirrors the slug-namespace split). The
52
- * `do` selection only ever produces `task`/`prd`; the `advance` selection ALSO
52
+ * `do` selection only ever produces `task`/`spec`; the `advance` selection ALSO
53
53
  * produces `observation` (the lifecycle triage pool, task
54
54
  * `advance-autopick-lifecycle-pools`), so a selected lifecycle item carries which
55
55
  * rung the driver dispatches to. The widening is BACKWARD-COMPATIBLE: `do` never
@@ -76,13 +76,13 @@ export interface SelectedItem {
76
76
  /**
77
77
  * A lifecycle-pool selected item (task `advance-autopick-lifecycle-pools`). It is
78
78
  * a {@link SelectedItem} — the same shape — carrying the lifecycle namespace
79
- * (`observation` for triage; `task`/`prd` for a `needsAnswers`-blocked item the
79
+ * (`observation` for triage; `task`/`spec` for a `needsAnswers`-blocked item the
80
80
  * tick will surface/apply). A distinct alias names the lifecycle pools at the
81
81
  * call sites WITHOUT a structural difference (the discriminator is `namespace`).
82
82
  */
83
83
  export type LifecycleSelectedItem = SelectedItem;
84
84
 
85
- /** A prd candidate for the tasking pool, before the eligibility gate runs. */
85
+ /** A spec candidate for the tasking pool, before the eligibility gate runs. */
86
86
  export interface SpecCandidate {
87
87
  repoPath: string;
88
88
  slug: string;
@@ -91,21 +91,21 @@ export interface SpecCandidate {
91
91
  taskedAfter: string[];
92
92
  }
93
93
 
94
- /** Inputs to {@link taskableSpecs}: the raw prd pool + the gate context. */
94
+ /** Inputs to {@link taskableSpecs}: the raw spec pool + the gate context. */
95
95
  export interface TaskableSpecsInput {
96
- /** Every prd enumerated from `work/prds/ready/` (the auto-task candidate source). */
96
+ /** Every spec enumerated from `work/specs/ready/` (the auto-task candidate source). */
97
97
  candidates: SpecCandidate[];
98
- /** Slugs whose prd resides in `work/prds/tasked/` (resolves `taskedAfter`). */
98
+ /** Slugs whose spec resides in `work/specs/tasked/` (resolves `taskedAfter`). */
99
99
  taskedSlugs: Set<string>;
100
100
  /** The repo's resolved `autoTask` policy (`autoslice-gate`'s per-repo key). */
101
101
  autoTask: boolean;
102
102
  }
103
103
 
104
104
  /**
105
- * Filter a raw prd pool down to the TASKABLE prds, in declaration order, using
105
+ * Filter a raw spec pool down to the TASKABLE prds, in declaration order, using
106
106
  * `autoslice-gate`'s pure predicate ({@link resolveTaskingEligibility}) — NOT a
107
- * reinvented eligibility model. A prd is taskable iff `needsAnswers !== true &&
108
- * humanOnly !== true && autoTask` AND every `taskedAfter` prd is already tasked.
107
+ * reinvented eligibility model. A spec is taskable iff `needsAnswers !== true &&
108
+ * humanOnly !== true && autoTask` AND every `taskedAfter` spec is already tasked.
109
109
  * Pure: no I/O (the caller reads the pool through `ledgerRead.resolveSpecPool`).
110
110
  */
111
111
  export function taskableSpecs(input: TaskableSpecsInput): SpecCandidate[] {
@@ -171,7 +171,7 @@ export interface SelectPrioritisedInput {
171
171
  */
172
172
  lifecycle?: SelectedLifecyclePools;
173
173
  /**
174
- * The HELD-SLUG set to SUBTRACT from the `build` pool (eligible TASKS) — prd
174
+ * The HELD-SLUG set to SUBTRACT from the `build` pool (eligible TASKS) — spec
175
175
  * `ledger-status-per-item-lock-refs` US #15, task
176
176
  * `claim-acquires-unified-lock-no-body-move`. A task whose per-item lock is
177
177
  * currently held is dropped from selection, so the eligible pool is "in
@@ -198,7 +198,7 @@ export interface SelectedLifecyclePools {
198
198
  * Build the ordered, counted list of items to do across the (up to) FIVE pools,
199
199
  * applying the configurable {@link selectionOrder} with `apply` PINNED FIRST and
200
200
  * the count bound. The task pool is selected via the EXISTING
201
- * {@link selectCandidates} (the shared primitive `run` uses); the prd pool is the
201
+ * {@link selectCandidates} (the shared primitive `run` uses); the spec pool is the
202
202
  * pre-filtered {@link taskableSpecs} output; the lifecycle pools (`apply` /
203
203
  * `surface` / `triage`) are caller-built (none for `do`).
204
204
  *
@@ -235,7 +235,7 @@ export function selectPrioritised(
235
235
 
236
236
  // The per-pool item lists, keyed by the orderable pool name. NOTE the
237
237
  // vocabulary bridge: `build` = the eligible-TASK pool (namespace `task`),
238
- // `task` = the taskable-prd pool (namespace `prd`) — the action names, not the
238
+ // `task` = the taskable-spec pool (namespace `spec`) — the action names, not the
239
239
  // item namespaces (task `advance-selection-order-config`).
240
240
  const byPool: Record<SelectionPool, SelectedItem[]> = {
241
241
  build: buildItems,
@@ -174,7 +174,7 @@ export function applyAtomic(options: ApplyAtomicOptions): ApplyAtomicResult {
174
174
  writeFileSync(itemAbs, setNeedsAnswersMarker(itemBody, true));
175
175
  }
176
176
  mkdirSync(dirname(sidecarAbs), {recursive: true});
177
- writeFileSync(sidecarAbs, serialiseSidecar(sidecar));
177
+ writeFileSync(sidecarAbs, serialiseSidecar(sidecar, {repoRoot: cwd}));
178
178
  touched.push(sidecarPath);
179
179
  }
180
180
 
package/src/sidecar.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import {existsSync} from 'node:fs';
2
+ import {join} from 'node:path';
1
3
  import {parseSlugArg, type SlugNamespace} from './slug-namespace.js';
2
- import {workItemRel} from './work-layout.js';
4
+ import {workItemRel, type WorkFolderKey} from './work-layout.js';
3
5
 
4
6
  /**
5
- * The **question/answer SIDECAR contract** (prd `advance-loop`, task
7
+ * The **question/answer SIDECAR contract** (spec `advance-loop`, task
6
8
  * `advance-sidecar-contract`) — the one genuinely-new piece of the `advance`
7
9
  * family. A strict, tooling-OWNED per-item file `work/questions/<type>-<slug>.md`
8
10
  * carrying per-entry answered-state, so the tooling fully owns the Q&A artifact
@@ -72,7 +74,7 @@ import {workItemRel} from './work-layout.js';
72
74
  * The item-types a sidecar can key onto (the slug-namespace + obs).
73
75
  *
74
76
  * HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
75
- * contract step): the legacy `'prd'` type member is GONE — the parent-spec type
77
+ * contract step): the legacy ''prd'' type member is GONE — the parent-spec type
76
78
  * is `'spec'` only. A `spec:<slug>` identity keys onto the `spec-<slug>`
77
79
  * lock/sidecar entry. The on-disk `prd-<slug>.md` sidecar FILE (dorfl's
78
80
  * not-yet-converted data) is still probed by {@link sidecarPathCandidates} as a
@@ -88,13 +90,19 @@ export type SidecarType = 'spec' | 'task' | 'observation';
88
90
  * sniffing the shape of another field. Absent ⇒ the existing binary content
89
91
  * entry (every pre-`kind` sidecar parses + renders byte-identically).
90
92
  *
91
- * INTERIM PRIMITIVE REMOVE when question sidecars move to KIND-BASED
92
- * SUBFOLDERS (`work/questions/merge/`, ), where the folder ENCODES the kind
93
- * and this per-entry field is redundant. See the observation
94
- * `questions-folder-rename-and-kind-axis-prefix-vs-subfolder-2026-06-21` + idea
95
- * `folder-taxonomy-and-prd-edit-handshake`. Built deliberately as a single
96
- * typed field read in exactly ONE place (the apply dispatch, a later task) so
97
- * the folder-cutover can DELETE it in one move.
93
+ * A TYPED FIELD is the PERMANENT home for the kind axis (not an interim step
94
+ * toward kind subfolders). Encoding kind in the PATH either a filename prefix
95
+ * (`merge-<type>-<slug>.md`) or a subfolder (`work/questions/merge/…`) was
96
+ * deliberately REJECTED: kind is a MUTABLE axis (one item can carry a `spec`
97
+ * question pre-build, then a `merge` question at land time), but `sidecarPathFor`
98
+ * is a pure function of item IDENTITY alone and does not know the kind, so a
99
+ * path that encodes kind would make identity-only lookups SILENTLY miss a
100
+ * sidecar filed under a different kind. So kind lives INSIDE the file (this
101
+ * field), the path stays identity-keyed, and per-kind queues are RENDERED by the
102
+ * tool (`status`/`scan`) rather than by the directory tree. See the observation
103
+ * `questions-folder-rename-and-kind-axis-prefix-vs-subfolder-2026-06-21` (its
104
+ * round-2 conclusion + the recorded answers) and `SURFACE-PROTOCOL.md`. The
105
+ * field is read in exactly ONE place (the apply dispatch).
98
106
  *
99
107
  * A mistyped/unknown `kind=` token parses to `undefined` (silent-on-malformed,
100
108
  * mirroring the retired `disposition` precedent), never a throw, never a
@@ -208,7 +216,7 @@ function typeForNamespace(
208
216
  if (explicit === 'task') {
209
217
  return 'task';
210
218
  }
211
- // `parseSlugArg` only knows `task:`/`prd:`; the sidecar adds the
219
+ // `parseSlugArg` only knows `task:`/`spec:`; the sidecar adds the
212
220
  // `observation:` namespace (`obs:` is the CLI alias the verb resolves; the
213
221
  // sidecar stores the canonical `observation`). A bare slug (no prefix)
214
222
  // defaults to the task namespace, matching the resolver's "bare = task".
@@ -267,14 +275,14 @@ export function sidecarPathFor(identity: string): string {
267
275
  * canonical {@link sidecarPathFor} path FIRST, then any legacy-data fallback that
268
276
  * may still be on disk before the migration command converts it.
269
277
  *
270
- * MIGRATE step (prd `prd-to-spec-vocabulary-cutover-and-migration-command`): the
278
+ * MIGRATE step (spec `prd-to-spec-vocabulary-cutover-and-migration-command`): the
271
279
  * producer side now emits `spec:<slug>`, so {@link sidecarPathFor} resolves
272
280
  * `work/questions/spec-<slug>.md`. But the ON-DISK sidecar is still the legacy
273
281
  * `work/questions/prd-<slug>.md` until `dorfl prd-to-spec` renames the DATA. So a
274
282
  * `spec`-typed identity ALSO probes the legacy `prd-<slug>.md` as a fallback, and
275
283
  * a reader takes the FIRST candidate that exists. This is a FILE-PATH DATA alias
276
284
  * the migration command removes (it converts `prd-<slug>.md → spec-<slug>.md` on
277
- * disk); it is NOT the `SlugNamespace`/`SidecarType` `'prd'` type member. Every
285
+ * disk); it is NOT the `SlugNamespace`/`SidecarType` ''prd'' type member. Every
278
286
  * non-`spec` type has a single candidate (its canonical path), unchanged.
279
287
  */
280
288
  export function sidecarPathCandidates(identity: string): string[] {
@@ -291,6 +299,77 @@ export function sidecarPathCandidates(identity: string): string[] {
291
299
  /** The fixed answer marker the human types prose under. */
292
300
  const ANSWER_MARKER = '**Your answer** (write below this line):';
293
301
 
302
+ /**
303
+ * The lifecycle folders a given item type may currently reside in — the
304
+ * search set the {@link serialiseSidecar} human-visible link line scans to
305
+ * locate the item at write-time. Deliberately INCLUSIVE of the terminal
306
+ * folders (`done`, `cancelled`, `prds-dropped`) so a sidecar still-being-
307
+ * serialised for a finished item still emits a clickable link. Kept LOCAL to
308
+ * this module (rather than reused from `advance.ts`) because this set is the
309
+ * "where might the item CURRENTLY be on disk?" question, which is broader
310
+ * than advance's rung-classifier reach.
311
+ *
312
+ * DECISION (sidecar-visible-item-link): the task set is the four DURABLE
313
+ * folders `tasks-ready` / `done` / `cancelled` / `tasks-backlog` and
314
+ * deliberately EXCLUDES `in-progress` / `needs-attention`. Those two are NOT
315
+ * durable folders — they are retired transient lock-ref state (ADR
316
+ * `needs-attention-folder-cutover-followup-nits`, see the `TASK_LIFECYCLE_FOLDERS`
317
+ * JSDoc in `work-layout.ts`): a stuck task's BODY rests in `tasks/ready/`
318
+ * while the lock carries `state: stuck`, so no task body ever lives under
319
+ * `work/in-progress/` or `work/needs-attention/` for this scan to find. An
320
+ * earlier WIP of this task listed both; narrowing to the durable set matches
321
+ * the current on-disk reality and avoids scanning phantom folders.
322
+ * ALTERNATIVE considered: reuse `TASK_LIFECYCLE_FOLDERS` directly — rejected
323
+ * because it still carries the legacy `in-progress` entry and omits the
324
+ * `cancelled` / `tasks-backlog` folders a sidecar link may need to reach.
325
+ */
326
+ const LINK_LIFECYCLE_FOLDERS: Record<SidecarType, readonly WorkFolderKey[]> = {
327
+ task: ['tasks-ready', 'done', 'cancelled', 'tasks-backlog'],
328
+ spec: ['specs-ready', 'specs-tasked', 'specs-proposed', 'specs-dropped'],
329
+ observation: ['observations'],
330
+ };
331
+
332
+ /**
333
+ * Look up the item's CURRENT on-disk repo-relative path by scanning the
334
+ * lifecycle folders its type may reside in, in a documented precedence order.
335
+ * Returns `undefined` when the item is not found in ANY folder — the
336
+ * serialiser's "harmless fallback" (omit the link) branch.
337
+ *
338
+ * PURE lookup: no throws, no side effects. Every call is a fresh scan — the
339
+ * link SELF-HEALS on the next `serialise` after a `git mv` between folders
340
+ * (identity-keyed sidecar; NO lock-step move).
341
+ */
342
+ function findItemRelPath(
343
+ repoRoot: string,
344
+ type: SidecarType,
345
+ slug: string,
346
+ ): string | undefined {
347
+ for (const folder of LINK_LIFECYCLE_FOLDERS[type]) {
348
+ const rel = workItemRel(folder, `${slug}.md`);
349
+ if (existsSync(join(repoRoot, rel))) {
350
+ return rel;
351
+ }
352
+ }
353
+ return undefined;
354
+ }
355
+
356
+ /**
357
+ * Render the human-visible Markdown link line, from the sidecar's fixed path
358
+ * (`work/questions/<type>-<slug>.md`) to the item's current `work/<folder>/
359
+ * <slug>.md`. Since both live under `work/`, the sidecar-relative link is
360
+ * `../<folder-name>/<slug>.md` — the `..` climbs out of `questions/` and the
361
+ * `<folder-name>` (which may contain a `/`, e.g. `tasks/ready`) drops back in.
362
+ */
363
+ function renderItemLinkLine(item: string, itemRel: string): string {
364
+ // itemRel is `work/<folder-name>/<slug>.md`; strip the leading `work/` and
365
+ // prefix with `../` so the link is relative to `work/questions/<file>.md`.
366
+ const prefix = 'work/';
367
+ const target = itemRel.startsWith(prefix)
368
+ ? `../${itemRel.slice(prefix.length)}`
369
+ : itemRel;
370
+ return `Item: [\`${item}\`](${target})`;
371
+ }
372
+
294
373
  // --- Parse ----------------------------------------------------------------
295
374
 
296
375
  /** Pull the next monotonic id given the highest existing id number. */
@@ -582,7 +661,10 @@ function blockquote(value: string): string[] {
582
661
  * `parseSidecar(serialiseSidecar(m))` recovers an equal MODEL; re-serialising
583
662
  * canonicalises the text.
584
663
  */
585
- export function serialiseSidecar(model: SidecarModel): string {
664
+ export function serialiseSidecar(
665
+ model: SidecarModel,
666
+ options: SerialiseSidecarOptions = {},
667
+ ): string {
586
668
  const out: string[] = [];
587
669
  const identityParts = [
588
670
  `item=${model.item}`,
@@ -592,6 +674,21 @@ export function serialiseSidecar(model: SidecarModel): string {
592
674
  ];
593
675
  out.push(`<!-- dorfl-sidecar: ${identityParts.join(' ')} -->`);
594
676
 
677
+ // Human-visible Markdown link line — placed AFTER the identity comment and
678
+ // BEFORE the first `## ` heading, i.e. in the parser's ignored preamble
679
+ // region. Regenerated on every serialise from the item's CURRENT on-disk
680
+ // location; NEVER round-tripped through the parsed model (the link line is
681
+ // write-only cosmetic output, per the source observation). If the item is
682
+ // not resolvable on disk, we simply OMIT the line (harmless fallback) — a
683
+ // broken link would be more confusing than no link.
684
+ if (options.repoRoot !== undefined) {
685
+ const itemRel = findItemRelPath(options.repoRoot, model.type, model.slug);
686
+ if (itemRel !== undefined) {
687
+ out.push('');
688
+ out.push(renderItemLinkLine(model.item, itemRel));
689
+ }
690
+ }
691
+
595
692
  model.entries.forEach((entry) => {
596
693
  out.push('');
597
694
  const heading = entry.id.replace(/^q/, 'Q');
@@ -634,6 +731,36 @@ export function serialiseSidecar(model: SidecarModel): string {
634
731
  return out.join('\n');
635
732
  }
636
733
 
734
+ /**
735
+ * Options for {@link serialiseSidecar}. The optional `repoRoot` opts the
736
+ * caller into emitting the human-visible Markdown link line at the top of
737
+ * the sidecar (pointing at the item's current `work/<folder>/<slug>.md`).
738
+ * Callers that don't have a repo root (e.g. pure format tests) simply omit
739
+ * it and the serialiser emits no link — the parse is unaffected either way.
740
+ *
741
+ * DECISION (sidecar-visible-item-link): `repoRoot` is OPTIONAL and defaults
742
+ * to NO link, rather than being a required argument threaded through every
743
+ * caller. Only the two WRITING call sites (`applyAtomic` in
744
+ * `sidecar-apply.ts`, `persistSurfacedQuestions` in `surface-persist.ts`)
745
+ * pass `cwd`; any other current/future caller of `serialiseSidecar` silently
746
+ * emits no link line. This keeps the many pure-format / round-trip tests
747
+ * trivial (they need no on-disk repo) and keeps the link a write-only
748
+ * cosmetic concern of the persist paths. TRADE-OFF: a new writing caller must
749
+ * remember to pass `repoRoot` to get the link; ALTERNATIVE considered — make
750
+ * it required — rejected because it would force every format-only caller and
751
+ * test to fabricate a repo root purely to reach the same no-link output.
752
+ */
753
+ export interface SerialiseSidecarOptions {
754
+ /**
755
+ * Absolute or relative path to the repository root, so the serialiser can
756
+ * scan `work/<lifecycle-folder>/<slug>.md` for the item and render a
757
+ * clickable relative link line. When omitted (or when the item cannot be
758
+ * located), the link line is omitted — a harmless fallback, never a
759
+ * broken link.
760
+ */
761
+ repoRoot?: string;
762
+ }
763
+
637
764
  // --- Append ---------------------------------------------------------------
638
765
 
639
766
  /** A new question to append (no id — the appender mints the next monotonic id). */
@@ -43,8 +43,8 @@ import {ledgerRead, type LedgerReadStrategy} from './ledger-read.js';
43
43
  * {@link resolveAdvanceArg}).
44
44
  *
45
45
  * HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
46
- * contract step): the legacy `'prd'` member is GONE — the parent-spec namespace
47
- * is `'spec'` only. A `prd:<slug>` arg no longer parses to a namespace (it falls
46
+ * contract step): the legacy ''prd'' member is GONE — the parent-spec namespace
47
+ * is `'spec'` only. A ''prd:<slug>'' arg no longer parses to a namespace (it falls
48
48
  * through to a bare literal slug), and a `work/prd-<slug>` branch ref no longer
49
49
  * parses. No back-compat alias (the clean-break stance).
50
50
  */
@@ -102,7 +102,7 @@ export function workBranchRef(
102
102
  * anchors the optional producer prefix BEFORE the type alternation, so
103
103
  * `work/intake-task-foo` resolves to
104
104
  * `{producer:'intake', namespace:'task', slug:'foo'}` (the `slug` never
105
- * swallows the `intake-`/`task-` prefixes). The old `slice`/`brief`/`prd` types
105
+ * swallows the `intake-`/`task-` prefixes). The old ''slice''/''brief''/''prd'' types
106
106
  * are NOT in the alternation, so a pre-rename `work/prd-foo` ref returns
107
107
  * `undefined` (the clean-break stance: no migration-window alias).
108
108
  */
@@ -112,7 +112,7 @@ export function parseWorkBranchRef(
112
112
  | {producer?: BranchProducer; namespace: SlugNamespace; slug: string}
113
113
  | undefined {
114
114
  // HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
115
- // contract step): the type alternation is `task|spec` ONLY — the legacy `prd`
115
+ // contract step): the type alternation is `task|spec` ONLY — the legacy ''prd''
116
116
  // token is GONE, so a pre-rename `work/prd-<slug>` ref returns `undefined` (no
117
117
  // migration-window alias; the clean-break stance).
118
118
  const match = /^work\/(?:(intake)-)?(task|spec)-(.+)$/.exec(branch);
@@ -155,7 +155,7 @@ const TASK_PREFIX = 'task:';
155
155
  /**
156
156
  * The parent-spec namespace prefix: `spec:<slug>` → `{explicit: 'spec'}`. HARD
157
157
  * CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`): the
158
- * legacy `prd:` prefix is GONE — a `prd:<slug>` arg falls through to a bare
158
+ * legacy ''prd:'' prefix is GONE — a ''prd:<slug>'' arg falls through to a bare
159
159
  * literal slug (no namespace), the clean-break stance.
160
160
  */
161
161
  const SPEC_PREFIX = 'spec:';
@@ -184,8 +184,8 @@ export class SlugResolutionError extends Error {
184
184
  * slug. PURE string work, no existence check: `task:foo` → explicit task,
185
185
  * `spec:foo` → explicit spec, `foo` → bare (`explicit: undefined`). The prefix
186
186
  * match is case-sensitive and exact (`task:`/`spec:`); a slug like `tasked` is
187
- * NOT a prefix and stays bare. HARD CUTOVER: the legacy `prd:` prefix is not a
188
- * namespace prefix anymore — `prd:foo` stays a bare literal slug.
187
+ * NOT a prefix and stays bare. HARD CUTOVER: the legacy ''prd:'' prefix is not a
188
+ * namespace prefix anymore — ''prd:foo'' stays a bare literal slug.
189
189
  */
190
190
  export function parseSlugArg(arg: string): ParsedSlugArg {
191
191
  if (arg.startsWith(TASK_PREFIX)) {
@@ -263,7 +263,7 @@ export function resolveSlug(input: ResolveSlugInput): ResolvedSlug {
263
263
  const parsed = parseSlugArg(input.arg);
264
264
 
265
265
  if (parsed.explicit === 'observation') {
266
- // `do` spans the task/prd namespaces ONLY (build a task OR task a prd).
266
+ // `do` spans the task/spec namespaces ONLY (build a task OR task a spec).
267
267
  // The `observation` namespace is `advance`'s alone (triage). Reject it here
268
268
  // rather than let a `do obs:<slug>` resolve into a namespace `do` cannot act
269
269
  // on — point the human at the verb that owns it.
@@ -289,7 +289,7 @@ export function resolveSlug(input: ResolveSlugInput): ResolvedSlug {
289
289
  }
290
290
 
291
291
  /**
292
- * Resolve a slug argument for the `advance` verb (prd `advance-loop`, task
292
+ * Resolve a slug argument for the `advance` verb (spec `advance-loop`, task
293
293
  * `advance-verb-resolver`). `advance` is the SIBLING top-level verb (NOT a `do`
294
294
  * subcommand) that reuses this SAME shared `prefix:arg` resolver, EXTENDED with
295
295
  * the `observation` namespace `do` does not span:
@@ -362,6 +362,6 @@ export function resolveTaskOnlyArg(arg: string): string {
362
362
  );
363
363
  }
364
364
  // Bare or `task:` → the task slug. (A bare slug on a task-only command is
365
- // unambiguously the task; the prd namespace is unreachable here.)
365
+ // unambiguously the task; the spec namespace is unreachable here.)
366
366
  return parsed.slug;
367
367
  }
@@ -10,7 +10,7 @@ import {
10
10
  } from './work-layout.js';
11
11
 
12
12
  /**
13
- * The read-only **"is this spec complete?"** core query (prd `issue-intake`, US #8 —
13
+ * The read-only **"is this spec complete?"** core query (spec `issue-intake`, US #8 —
14
14
  * the closure-linkage half). Given a spec slug + a `work/` tree, a spec is COMPLETE
15
15
  * iff there is **≥1 task carrying `spec:<slug>`** AND **all such tasks reside in
16
16
  * `work/done/`**. Pure `work/`-folder logic — no seam, no git, no `gh`, no mutation.
@@ -27,12 +27,11 @@ import {
27
27
  * claim ledger (`ledger-read.ts` resolves claim-STATE, a different concern). It
28
28
  * reuses {@link parseFrontmatter} (the `spec:` field) rather than hand-rolling a YAML
29
29
  * parse, and scans the task lifecycle folders directly: `work/tasks/ready/`,
30
- * `work/in-progress/`, `work/needs-attention/`, and `work/done/`. A task that has
31
- * NOT yet landed in `work/done/` (still in backlog / in-progress / needs-attention)
32
- * means the spec is not yet complete.
30
+ * `work/in-progress/`, and `work/done/`. A task that has NOT yet landed in
31
+ * `work/done/` (still in backlog / in-progress) means the spec is not yet complete.
33
32
  */
34
33
 
35
- /** The task lifecycle folders a `prd:<slug>` task can reside in. */
34
+ /** The task lifecycle folders a `spec:<slug>` task can reside in. */
36
35
  const TASK_FOLDERS = TASK_LIFECYCLE_FOLDERS;
37
36
 
38
37
  /** Where a task resides — the folder name under `work/`. */