dorfl 0.1.2 → 0.2.1

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 (364) hide show
  1. package/dist/advance-drivers.d.ts +1 -1
  2. package/dist/advance-drivers.d.ts.map +1 -1
  3. package/dist/advance-drivers.js +15 -3
  4. package/dist/advance-drivers.js.map +1 -1
  5. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  6. package/dist/advance-lifecycle-template.js +76 -4
  7. package/dist/advance-lifecycle-template.js.map +1 -1
  8. package/dist/advance-treeless-publish.d.ts +23 -0
  9. package/dist/advance-treeless-publish.d.ts.map +1 -1
  10. package/dist/advance-treeless-publish.js +41 -0
  11. package/dist/advance-treeless-publish.js.map +1 -1
  12. package/dist/advance.d.ts +59 -7
  13. package/dist/advance.d.ts.map +1 -1
  14. package/dist/advance.js +389 -82
  15. package/dist/advance.js.map +1 -1
  16. package/dist/advancing-lock.d.ts +30 -2
  17. package/dist/advancing-lock.d.ts.map +1 -1
  18. package/dist/advancing-lock.js +50 -0
  19. package/dist/advancing-lock.js.map +1 -1
  20. package/dist/agent-launch.d.ts +12 -0
  21. package/dist/agent-launch.d.ts.map +1 -1
  22. package/dist/agent-launch.js +22 -12
  23. package/dist/agent-launch.js.map +1 -1
  24. package/dist/agent-stop.d.ts +40 -2
  25. package/dist/agent-stop.d.ts.map +1 -1
  26. package/dist/agent-stop.js +30 -2
  27. package/dist/agent-stop.js.map +1 -1
  28. package/dist/apply-decide.d.ts +17 -3
  29. package/dist/apply-decide.d.ts.map +1 -1
  30. package/dist/apply-decide.js +36 -7
  31. package/dist/apply-decide.js.map +1 -1
  32. package/dist/apply-merge-action.d.ts +17 -5
  33. package/dist/apply-merge-action.d.ts.map +1 -1
  34. package/dist/apply-merge-action.js +43 -8
  35. package/dist/apply-merge-action.js.map +1 -1
  36. package/dist/apply-persist.d.ts +57 -26
  37. package/dist/apply-persist.d.ts.map +1 -1
  38. package/dist/apply-persist.js +166 -37
  39. package/dist/apply-persist.js.map +1 -1
  40. package/dist/apply-stuck-action.d.ts +151 -0
  41. package/dist/apply-stuck-action.d.ts.map +1 -0
  42. package/dist/apply-stuck-action.js +125 -0
  43. package/dist/apply-stuck-action.js.map +1 -0
  44. package/dist/brand.d.ts +12 -1
  45. package/dist/brand.d.ts.map +1 -1
  46. package/dist/brand.js +2 -1
  47. package/dist/brand.js.map +1 -1
  48. package/dist/cli-spinner.d.ts +1 -1
  49. package/dist/cli-spinner.js +1 -1
  50. package/dist/cli.d.ts +10 -0
  51. package/dist/cli.d.ts.map +1 -1
  52. package/dist/cli.js +320 -63
  53. package/dist/cli.js.map +1 -1
  54. package/dist/close-job-template.js +1 -1
  55. package/dist/complete.d.ts +6 -3
  56. package/dist/complete.d.ts.map +1 -1
  57. package/dist/complete.js +91 -37
  58. package/dist/complete.js.map +1 -1
  59. package/dist/config.d.ts +55 -9
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +56 -4
  62. package/dist/config.js.map +1 -1
  63. package/dist/continue-branch.d.ts.map +1 -1
  64. package/dist/continue-branch.js +22 -0
  65. package/dist/continue-branch.js.map +1 -1
  66. package/dist/decision-engine.d.ts +38 -10
  67. package/dist/decision-engine.d.ts.map +1 -1
  68. package/dist/decision-engine.js +10 -6
  69. package/dist/decision-engine.js.map +1 -1
  70. package/dist/do-autopick.d.ts.map +1 -1
  71. package/dist/do-autopick.js +11 -1
  72. package/dist/do-autopick.js.map +1 -1
  73. package/dist/do-config.d.ts +1 -1
  74. package/dist/do.d.ts +49 -56
  75. package/dist/do.d.ts.map +1 -1
  76. package/dist/do.js +419 -30
  77. package/dist/do.js.map +1 -1
  78. package/dist/env-config.d.ts.map +1 -1
  79. package/dist/env-config.js +12 -4
  80. package/dist/env-config.js.map +1 -1
  81. package/dist/failure-cause.d.ts +3 -2
  82. package/dist/failure-cause.d.ts.map +1 -1
  83. package/dist/failure-cause.js +28 -1
  84. package/dist/failure-cause.js.map +1 -1
  85. package/dist/format.d.ts +6 -6
  86. package/dist/format.d.ts.map +1 -1
  87. package/dist/format.js +10 -27
  88. package/dist/format.js.map +1 -1
  89. package/dist/frontmatter.d.ts +16 -4
  90. package/dist/frontmatter.d.ts.map +1 -1
  91. package/dist/frontmatter.js +27 -1
  92. package/dist/frontmatter.js.map +1 -1
  93. package/dist/gc.d.ts +65 -6
  94. package/dist/gc.d.ts.map +1 -1
  95. package/dist/gc.js +126 -9
  96. package/dist/gc.js.map +1 -1
  97. package/dist/github.d.ts +14 -0
  98. package/dist/github.d.ts.map +1 -1
  99. package/dist/github.js +73 -0
  100. package/dist/github.js.map +1 -1
  101. package/dist/harness.d.ts +25 -0
  102. package/dist/harness.d.ts.map +1 -1
  103. package/dist/harness.js.map +1 -1
  104. package/dist/index.d.ts +4 -1
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +2 -1
  107. package/dist/index.js.map +1 -1
  108. package/dist/install-ci-branch-protection.d.ts +132 -38
  109. package/dist/install-ci-branch-protection.d.ts.map +1 -1
  110. package/dist/install-ci-branch-protection.js +190 -48
  111. package/dist/install-ci-branch-protection.js.map +1 -1
  112. package/dist/install-ci-core.d.ts +21 -1
  113. package/dist/install-ci-core.d.ts.map +1 -1
  114. package/dist/install-ci-core.js +10 -3
  115. package/dist/install-ci-core.js.map +1 -1
  116. package/dist/install-ci-github.d.ts +32 -0
  117. package/dist/install-ci-github.d.ts.map +1 -1
  118. package/dist/install-ci-github.js +73 -0
  119. package/dist/install-ci-github.js.map +1 -1
  120. package/dist/install-skills.d.ts +123 -0
  121. package/dist/install-skills.d.ts.map +1 -0
  122. package/dist/install-skills.js +100 -0
  123. package/dist/install-skills.js.map +1 -0
  124. package/dist/intake-trigger-template.js +1 -1
  125. package/dist/intake.d.ts +17 -4
  126. package/dist/intake.d.ts.map +1 -1
  127. package/dist/intake.js +23 -7
  128. package/dist/intake.js.map +1 -1
  129. package/dist/integration-core.d.ts +10 -7
  130. package/dist/integration-core.d.ts.map +1 -1
  131. package/dist/integration-core.js +174 -60
  132. package/dist/integration-core.js.map +1 -1
  133. package/dist/integrator.d.ts +1 -1
  134. package/dist/integrator.d.ts.map +1 -1
  135. package/dist/integrator.js +19 -3
  136. package/dist/integrator.js.map +1 -1
  137. package/dist/isolation.d.ts +2 -2
  138. package/dist/isolation.d.ts.map +1 -1
  139. package/dist/isolation.js +9 -0
  140. package/dist/isolation.js.map +1 -1
  141. package/dist/item-lock.d.ts +196 -129
  142. package/dist/item-lock.d.ts.map +1 -1
  143. package/dist/item-lock.js +329 -254
  144. package/dist/item-lock.js.map +1 -1
  145. package/dist/ledger-write.d.ts +31 -26
  146. package/dist/ledger-write.d.ts.map +1 -1
  147. package/dist/ledger-write.js +100 -122
  148. package/dist/ledger-write.js.map +1 -1
  149. package/dist/lifecycle-gather.d.ts +17 -0
  150. package/dist/lifecycle-gather.d.ts.map +1 -1
  151. package/dist/lifecycle-gather.js +7 -1
  152. package/dist/lifecycle-gather.js.map +1 -1
  153. package/dist/lifecycle-pools.d.ts +40 -3
  154. package/dist/lifecycle-pools.d.ts.map +1 -1
  155. package/dist/lifecycle-pools.js +26 -6
  156. package/dist/lifecycle-pools.js.map +1 -1
  157. package/dist/merge-question-surfacer.d.ts +15 -1
  158. package/dist/merge-question-surfacer.d.ts.map +1 -1
  159. package/dist/merge-question-surfacer.js +18 -3
  160. package/dist/merge-question-surfacer.js.map +1 -1
  161. package/dist/migrate-stuck-locks.d.ts +129 -0
  162. package/dist/migrate-stuck-locks.d.ts.map +1 -0
  163. package/dist/migrate-stuck-locks.js +355 -0
  164. package/dist/migrate-stuck-locks.js.map +1 -0
  165. package/dist/needs-attention.d.ts +293 -16
  166. package/dist/needs-attention.d.ts.map +1 -1
  167. package/dist/needs-attention.js +532 -57
  168. package/dist/needs-attention.js.map +1 -1
  169. package/dist/orphan-sidecar.d.ts +8 -4
  170. package/dist/orphan-sidecar.d.ts.map +1 -1
  171. package/dist/orphan-sidecar.js +35 -2
  172. package/dist/orphan-sidecar.js.map +1 -1
  173. package/dist/pi-harness.d.ts +16 -0
  174. package/dist/pi-harness.d.ts.map +1 -1
  175. package/dist/pi-harness.js +82 -2
  176. package/dist/pi-harness.js.map +1 -1
  177. package/dist/placement.d.ts +3 -3
  178. package/dist/placement.js +1 -1
  179. package/dist/prd-to-spec.d.ts.map +1 -1
  180. package/dist/prd-to-spec.js +9 -5
  181. package/dist/prd-to-spec.js.map +1 -1
  182. package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
  183. package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
  184. package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
  185. package/dist/protocol/TASKING-PROTOCOL.md +3 -1
  186. package/dist/protocol/WORK-CONTRACT.md +22 -18
  187. package/dist/protocol/task-template.md +1 -1
  188. package/dist/reap-branches.d.ts +12 -9
  189. package/dist/reap-branches.d.ts.map +1 -1
  190. package/dist/reap-branches.js +25 -7
  191. package/dist/reap-branches.js.map +1 -1
  192. package/dist/recover-isolated.d.ts +12 -0
  193. package/dist/recover-isolated.d.ts.map +1 -1
  194. package/dist/recover-isolated.js +5 -0
  195. package/dist/recover-isolated.js.map +1 -1
  196. package/dist/repo-config.d.ts +23 -2
  197. package/dist/repo-config.d.ts.map +1 -1
  198. package/dist/repo-config.js +53 -7
  199. package/dist/repo-config.js.map +1 -1
  200. package/dist/repo-mirror.d.ts.map +1 -1
  201. package/dist/repo-mirror.js +18 -2
  202. package/dist/repo-mirror.js.map +1 -1
  203. package/dist/review-verdict.js +1 -1
  204. package/dist/review-verdict.js.map +1 -1
  205. package/dist/run.d.ts +1 -1
  206. package/dist/run.d.ts.map +1 -1
  207. package/dist/run.js +54 -19
  208. package/dist/run.js.map +1 -1
  209. package/dist/scan.d.ts +17 -8
  210. package/dist/scan.d.ts.map +1 -1
  211. package/dist/scan.js +22 -8
  212. package/dist/scan.js.map +1 -1
  213. package/dist/select-priority.d.ts +2 -2
  214. package/dist/select-priority.js +1 -1
  215. package/dist/select-priority.js.map +1 -1
  216. package/dist/sidecar-apply.js +1 -1
  217. package/dist/sidecar-apply.js.map +1 -1
  218. package/dist/sidecar.d.ts +45 -10
  219. package/dist/sidecar.d.ts.map +1 -1
  220. package/dist/sidecar.js +82 -2
  221. package/dist/sidecar.js.map +1 -1
  222. package/dist/skills/answer-questions/SKILL.md +89 -0
  223. package/dist/skills/capture-signal/SKILL.md +52 -0
  224. package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
  225. package/dist/skills/drive-tasks/SKILL.md +218 -0
  226. package/dist/skills/from-idea/SKILL.md +83 -0
  227. package/dist/skills/merge-prs/SKILL.md +70 -0
  228. package/dist/skills/orchestrate/SKILL.md +101 -0
  229. package/dist/skills/promote/SKILL.md +35 -0
  230. package/dist/skills/review/SKILL.md +16 -0
  231. package/dist/skills/setup/SKILL.md +258 -0
  232. package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
  233. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
  234. package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
  235. package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
  236. package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
  237. package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
  238. package/dist/skills/setup/protocol/spec-template.md +71 -0
  239. package/dist/skills/setup/protocol/task-template.md +65 -0
  240. package/dist/skills/surface-questions/SKILL.md +16 -0
  241. package/dist/skills/to-spec/SKILL.md +34 -0
  242. package/dist/skills/to-task/SKILL.md +19 -0
  243. package/dist/skills/triage-observations/SKILL.md +78 -0
  244. package/dist/skills/work/SKILL.md +51 -0
  245. package/dist/slug-namespace.d.ts +5 -5
  246. package/dist/slug-namespace.js +5 -5
  247. package/dist/slug-namespace.js.map +1 -1
  248. package/dist/spec-complete.d.ts.map +1 -1
  249. package/dist/spec-complete.js +2 -3
  250. package/dist/spec-complete.js.map +1 -1
  251. package/dist/start.d.ts.map +1 -1
  252. package/dist/start.js +51 -57
  253. package/dist/start.js.map +1 -1
  254. package/dist/surface-gate.d.ts.map +1 -1
  255. package/dist/surface-gate.js +10 -3
  256. package/dist/surface-gate.js.map +1 -1
  257. package/dist/surface-persist.js +1 -1
  258. package/dist/surface-persist.js.map +1 -1
  259. package/dist/tasker-review-loop.d.ts +4 -3
  260. package/dist/tasker-review-loop.d.ts.map +1 -1
  261. package/dist/tasker-review-loop.js.map +1 -1
  262. package/dist/tasking-lock.d.ts +2 -2
  263. package/dist/tasking-lock.d.ts.map +1 -1
  264. package/dist/tasking-lock.js +53 -32
  265. package/dist/tasking-lock.js.map +1 -1
  266. package/dist/tasking.d.ts +40 -6
  267. package/dist/tasking.d.ts.map +1 -1
  268. package/dist/tasking.js +224 -12
  269. package/dist/tasking.js.map +1 -1
  270. package/dist/triage-persist.d.ts +10 -6
  271. package/dist/triage-persist.d.ts.map +1 -1
  272. package/dist/triage-persist.js +34 -4
  273. package/dist/triage-persist.js.map +1 -1
  274. package/dist/vendor/incur/agents.d.ts +58 -0
  275. package/dist/vendor/incur/agents.d.ts.map +1 -0
  276. package/dist/vendor/incur/agents.js +343 -0
  277. package/dist/vendor/incur/agents.js.map +1 -0
  278. package/dist/verify-workflow-template.js +1 -1
  279. package/dist/watch-session.d.ts +11 -3
  280. package/dist/watch-session.d.ts.map +1 -1
  281. package/dist/watch-session.js +94 -7
  282. package/dist/watch-session.js.map +1 -1
  283. package/dist/work-layout.d.ts +7 -3
  284. package/dist/work-layout.d.ts.map +1 -1
  285. package/dist/work-layout.js +6 -3
  286. package/dist/work-layout.js.map +1 -1
  287. package/dist/workspace.d.ts +1 -1
  288. package/package.json +2 -2
  289. package/src/advance-drivers.ts +16 -3
  290. package/src/advance-lifecycle-template.ts +89 -4
  291. package/src/advance-treeless-publish.ts +45 -0
  292. package/src/advance.ts +467 -87
  293. package/src/advancing-lock.ts +96 -1
  294. package/src/agent-launch.ts +37 -12
  295. package/src/agent-stop.ts +60 -2
  296. package/src/apply-decide.ts +36 -7
  297. package/src/apply-merge-action.ts +45 -7
  298. package/src/apply-persist.ts +228 -54
  299. package/src/apply-stuck-action.ts +260 -0
  300. package/src/brand.ts +14 -2
  301. package/src/cli-spinner.ts +1 -1
  302. package/src/cli.ts +401 -70
  303. package/src/close-job-template.ts +1 -1
  304. package/src/complete.ts +110 -52
  305. package/src/config.ts +107 -13
  306. package/src/continue-branch.ts +22 -0
  307. package/src/decision-engine.ts +55 -17
  308. package/src/do-autopick.ts +12 -1
  309. package/src/do-config.ts +1 -1
  310. package/src/do.ts +511 -38
  311. package/src/env-config.ts +12 -4
  312. package/src/failure-cause.ts +30 -1
  313. package/src/format.ts +10 -30
  314. package/src/frontmatter.ts +35 -5
  315. package/src/gc.ts +172 -9
  316. package/src/github.ts +78 -0
  317. package/src/harness.ts +25 -0
  318. package/src/index.ts +10 -0
  319. package/src/install-ci-branch-protection.ts +282 -57
  320. package/src/install-ci-core.ts +30 -3
  321. package/src/install-ci-github.ts +86 -0
  322. package/src/install-skills.ts +166 -0
  323. package/src/intake-trigger-template.ts +1 -1
  324. package/src/intake.ts +46 -11
  325. package/src/integration-core.ts +199 -65
  326. package/src/integrator.ts +22 -9
  327. package/src/isolation.ts +11 -2
  328. package/src/item-lock.ts +485 -341
  329. package/src/ledger-write.ts +135 -150
  330. package/src/lifecycle-gather.ts +29 -6
  331. package/src/lifecycle-pools.ts +67 -9
  332. package/src/merge-question-surfacer.ts +28 -4
  333. package/src/migrate-stuck-locks.ts +451 -0
  334. package/src/needs-attention.ts +857 -84
  335. package/src/orphan-sidecar.ts +47 -6
  336. package/src/pi-harness.ts +82 -2
  337. package/src/placement.ts +3 -3
  338. package/src/prd-to-spec.ts +13 -5
  339. package/src/reap-branches.ts +38 -14
  340. package/src/recover-isolated.ts +17 -0
  341. package/src/repo-config.ts +55 -6
  342. package/src/repo-mirror.ts +23 -1
  343. package/src/review-verdict.ts +1 -1
  344. package/src/run.ts +64 -18
  345. package/src/scan.ts +22 -8
  346. package/src/select-priority.ts +3 -3
  347. package/src/sidecar-apply.ts +1 -1
  348. package/src/sidecar.ts +138 -11
  349. package/src/slug-namespace.ts +7 -7
  350. package/src/spec-complete.ts +2 -3
  351. package/src/start.ts +53 -59
  352. package/src/surface-gate.ts +10 -3
  353. package/src/surface-persist.ts +1 -1
  354. package/src/tasker-review-loop.ts +4 -3
  355. package/src/tasking-lock.ts +52 -36
  356. package/src/tasking.ts +261 -17
  357. package/src/triage-persist.ts +56 -12
  358. package/src/vendor/incur/LICENSE +21 -0
  359. package/src/vendor/incur/README.md +19 -0
  360. package/src/vendor/incur/agents.ts +392 -0
  361. package/src/verify-workflow-template.ts +1 -1
  362. package/src/watch-session.ts +102 -7
  363. package/src/work-layout.ts +6 -3
  364. package/src/workspace.ts +1 -1
package/src/tasking.ts CHANGED
@@ -53,7 +53,7 @@ import type {ReviewGate} from './review-gate.js';
53
53
  * (`tasking-eligibility.ts`) and the tasking LOCK (`tasking-lock.ts`) together to
54
54
  * task a spec into `work/tasks/backlog/` STAGED items (task
55
55
  * `pre-backlog-staging-folder-and-promote-step-a` — the runner-owned promotion
56
- * moves them `pre-backlog/ → backlog/` later), with the RUNNER owning every git-state
56
+ * moves them `tasks/backlog/ → tasks/ready/` later), with the RUNNER owning every git-state
57
57
  * transition. This is the spec branch of the `do` worker (ADR
58
58
  * `command-surface-and-journeys.md` §3/§3a), NOT a standalone `task` command;
59
59
  * `do.ts` dispatches `resolved.namespace === 'spec'` here.
@@ -244,6 +244,18 @@ export interface PerformTaskOptions {
244
244
  * loop's {@link taskerLoopModel} — see the note there.
245
245
  */
246
246
  acceptanceReviewModel?: string;
247
+ /**
248
+ * **The cross-job merge-serialiser CAS-retry cap** (config `mergeRetries`, spec
249
+ * `land-time-reverify-and-parallel-merge-ceiling` Story 5 / Applied Answer q1
250
+ * (a)). Threaded VERBATIM into {@link performIntegration} so a wide-matrix CI's
251
+ * raised cap actually reaches the cross-job land queue (the CAS loop IS the
252
+ * queue across separate jobs) on the `do spec:<slug>` tasking-transition path
253
+ * too. Resolved ONCE at the entry point through the gate-family precedence chain
254
+ * (flag > env > per-repo > global > default), same as `complete`/`do`/`run`.
255
+ * Unset ⇒ falls through to the engine's `DEFAULT_MERGE_RETRIES = 1000`
256
+ * (byte-for-byte unchanged).
257
+ */
258
+ mergeRetries?: number;
247
259
  /** Injectable lock seam (tests stub acquire/release). Defaults to the real CAS. */
248
260
  lock?: TaskingLockSeam;
249
261
  /**
@@ -255,10 +267,10 @@ export interface PerformTaskOptions {
255
267
  * resolver (`src/placement.ts`). The resolver overlays an EXPLICIT operator
256
268
  * flag ({@link explicitTasksLandIn}, top) and the UNTRUSTED-ORIGIN force
257
269
  * (`originTrust: untrusted` ⇒ staging) on top. Unset ⇒ the resolver's
258
- * built-in floor applies (`staging` = `pre-backlog/`, the conservative
270
+ * built-in floor applies (`staging` = `tasks/backlog/`, the conservative
259
271
  * landing that preserves zero behaviour change for the normal path).
260
272
  */
261
- tasksLandIn?: 'pre-backlog' | 'ready';
273
+ tasksLandIn?: 'backlog' | 'ready';
262
274
  /**
263
275
  * **The OPERATOR's EXPLICIT task-placement override** (the TOP precedence
264
276
  * rung). When set, the runner-deterministic resolver lands the tasks HERE
@@ -268,7 +280,7 @@ export interface PerformTaskOptions {
268
280
  * force-key"). Set ONLY when the operator typed `--tasks-land-in <where>`;
269
281
  * never when the value came from config.
270
282
  */
271
- explicitTasksLandIn?: 'pre-backlog' | 'ready';
283
+ explicitTasksLandIn?: 'backlog' | 'ready';
272
284
  /**
273
285
  * **The tasker review→edit→converge LOOP** (`slicer-review-edit-loop`, GATES spec
274
286
  * `work/specs/ready/review.md` RESOLVED DESIGN — Shape 2 / insertion point A). When
@@ -322,10 +334,10 @@ const DEFAULT_ARBITER = 'origin';
322
334
  * task `pre-backlog-staging-folder-and-promote-step-a`, governing ADR
323
335
  * `placement-is-runner-deterministic-humanonly-is-agent-judgement`). The runner
324
336
  * lands the tasker's emitted task files HERE, NOT in `work/tasks/ready/`: an item
325
- * born in `pre-backlog/` is durable + readable but NOT in the agent-eligible
337
+ * born in `tasks/backlog/` is durable + readable but NOT in the agent-eligible
326
338
  * pool (`work/tasks/ready/` STILL means the pool — every reader is byte-for-byte
327
339
  * unchanged). A runner/human-owned promotion (`promoteFromPreBacklog` in
328
- * `needs-attention.ts`) moves an approved item `pre-backlog/ → backlog/` to make
340
+ * `needs-attention.ts`) moves an approved item `tasks/backlog/ → tasks/ready/` to make
329
341
  * it claimable. STEP A: ADDITIVE — no `work/tasks/ready/` reader changes here.
330
342
  */
331
343
  export const STAGED_TASKS_DIR = workFolderRel('tasks-backlog');
@@ -348,17 +360,19 @@ const TASK_PLACEMENT_SLOTS = {
348
360
  } as const;
349
361
 
350
362
  /**
351
- * Map the `tasksLandIn` value spelling (`pre-backlog` | `ready`) onto the
363
+ * Map the `tasksLandIn` value spelling (`backlog` | `ready`) onto the
352
364
  * resolver's lifecycle-generic side enum (`staging` | `pool`). Returns
353
365
  * `undefined` when no value is set, so the resolver's next precedence rung
354
- * applies (the built-in floor). The legacy `'backlog'`/`'todo'` pool spellings
355
- * are NOT accepted (clean break — the value was renamed `'backlog'` → `'todo'`
356
- * → `'ready'`, ADR `rename-task-pool-folder-todo-to-ready`).
366
+ * applies (the built-in floor). The legacy `'pre-backlog'`/`'todo'` spellings
367
+ * are NOT accepted (clean break — the staging value was renamed
368
+ * `'pre-backlog'` → `'backlog'`, and the pool value was renamed
369
+ * `'backlog'` → `'todo'` → `'ready'`, ADR
370
+ * `rename-task-pool-folder-todo-to-ready`).
357
371
  */
358
372
  function landingToSide(
359
- landing: 'pre-backlog' | 'ready' | undefined,
373
+ landing: 'backlog' | 'ready' | undefined,
360
374
  ): 'staging' | 'pool' | undefined {
361
- if (landing === 'pre-backlog') return 'staging';
375
+ if (landing === 'backlog') return 'staging';
362
376
  if (landing === 'ready') return 'pool';
363
377
  return undefined;
364
378
  }
@@ -661,6 +675,12 @@ export async function performTask(
661
675
  reviewGate: options.reviewGate,
662
676
  reviewModel: options.acceptanceReviewModel,
663
677
  reviewMaxRounds: 1,
678
+ // The cross-job merge-serialiser CAS-retry cap (config `mergeRetries`) —
679
+ // threaded so a wide-matrix CI's raised cap reaches the tasking-
680
+ // transition's land tail too (task
681
+ // `thread-merge-retries-cross-task-and-ratify-default`). Unset ⇒ falls
682
+ // through to the engine default (byte-for-byte unchanged).
683
+ mergeRetries: options.mergeRetries,
664
684
  // The EXPLICITLY-chosen integrate mode proceeds AS-IS on an APPROVE — a
665
685
  // `--merge` tasking run lands on main, `--propose` opens a PR. The tasking
666
686
  // path's merge-vs-propose decision is the `integration` mode the user typed;
@@ -670,6 +690,17 @@ export async function performTask(
670
690
  mode: options.integration ?? 'propose',
671
691
  noPR: options.noPR,
672
692
  providerInstance: options.providerInstance,
693
+ // PROPOSE-MODE PR BODY (task `slicing-pr-body-summary-threading`): mirror the
694
+ // BUILD path's body threading (`do.ts` — `body: agent.output`). The tasker
695
+ // agent produces no `LaunchResult.output` we can carry, but the slice-SET
696
+ // itself is a summary-worthy artifact: the emitted task slugs+titles, their
697
+ // coverage of the spec's user stories, the dependency graph (keystone +
698
+ // blockedBy edges), and any `needsAnswers` open questions the tasker review
699
+ // loop flagged. Compose it here and thread it into the shared core as `body`
700
+ // so the propose-mode PR carries the summary instead of degrading to
701
+ // `gh pr create --fill`. Empty-set (no emitted tasks) ⇒ undefined ⇒ the
702
+ // provider's `--fill` fallback (no regression on that edge).
703
+ body: composeTaskingProposeBody(slug, emitTasks),
673
704
  type: 'tasking',
674
705
  lifecycle: {
675
706
  // Read the PR title / commit summary from the held spec (before it moves).
@@ -1058,7 +1089,7 @@ async function stageTaskingLifecycle(params: {
1058
1089
  * snapshot (the branch-base state of `work/tasks/ready/`, taken BEFORE the agent
1059
1090
  * ran), any new file is removed from the worktree and any changed file is
1060
1091
  * checked back out to HEAD — so the subsequent `git add -A` cannot land it. The
1061
- * runner's commit then carries ONLY the explicit `pre-backlog/` placement.
1092
+ * runner's commit then carries ONLY the explicit `tasks/backlog/` placement.
1062
1093
  */
1063
1094
  async function scrubPoolDrift(
1064
1095
  cwd: string,
@@ -1122,8 +1153,9 @@ async function gitHard(
1122
1153
  /**
1123
1154
  * Build the needs-attention REASON for a task-SET ACCEPTANCE GATE block (task
1124
1155
  * `slice-acceptance-gate`): the fresh-context review of the produced set returned
1125
- * `block`, so the spec is routed to `work/needs-attention/` with the review's
1126
- * blocking findings as the body prose and NO tasks landed. Takes the core's
1156
+ * `block`, so the spec is marked stuck on its per-item lock (post lock-cutover —
1157
+ * `state: stuck` with this reason on the lock entry, no `work/needs-attention/`
1158
+ * folder write) and NO tasks land. Takes the core's
1127
1159
  * structured `reviewBlockReason` (the gate's blocking findings); falls back to a
1128
1160
  * generic line when absent. DISTINCT from the improver loop's
1129
1161
  * {@link decompositionUnclearReason} (which carries the loop's open questions).
@@ -1141,8 +1173,8 @@ function taskGateBlockedReason(
1141
1173
 
1142
1174
  /**
1143
1175
  * Build the needs-attention REASON for a decomposition-unclear loop verdict (the
1144
- * spec is routed to `work/needs-attention/` with these open questions, no guessed
1145
- * tasks). Prose only — recorded as the spec's needs-attention body block.
1176
+ * spec is marked stuck on its per-item lock with these open questions, no guessed
1177
+ * tasks). Prose only — recorded as the spec's stuck-lock reason.
1146
1178
  */
1147
1179
  function decompositionUnclearReason(slug: string, questions: string[]): string {
1148
1180
  const head =
@@ -1427,6 +1459,218 @@ function collectEmittedTasks(
1427
1459
  return out;
1428
1460
  }
1429
1461
 
1462
+ /**
1463
+ * Compose the propose-mode PR BODY for a slice-set (task
1464
+ * `slicing-pr-body-summary-threading`): mirror the BUILD path's body threading
1465
+ * (`do.ts` — `body: agent.output`) with a summary drawn from what the tasker
1466
+ * ALREADY has in hand — the emitted task files. The shared
1467
+ * {@link composeProposeBody} then wraps this prose with its runner-owned header.
1468
+ *
1469
+ * Sections (all suppressed when empty, so the body stays honest):
1470
+ * 1. A lead line naming the source spec + emitted task count.
1471
+ * 2. **Tasks** — one bullet per task: `slug — title`, plus `covers:` (which
1472
+ * spec user stories it maps to), `blockedBy:`, and `needsAnswers` if set.
1473
+ * 3. **Dependency graph** — the KEYSTONE(S) (tasks whose `blockedBy` names no
1474
+ * other task in this set, so they land first) and the `blockedBy` edges.
1475
+ * 4. **Needs answers** — any open questions the tasker review→edit loop
1476
+ * appended under `## Open questions` on `needsAnswers: true` tasks, so the
1477
+ * deferred seams are visible on the PR (not buried in task bodies).
1478
+ *
1479
+ * Returns `undefined` for the empty set (no emitted tasks) so the provider's
1480
+ * `gh pr create --fill` fallback still applies on that edge (matches the build
1481
+ * path's absent-body behaviour).
1482
+ */
1483
+ export function composeTaskingProposeBody(
1484
+ specSlug: string,
1485
+ emitTasks: Record<string, string>,
1486
+ ): string | undefined {
1487
+ const entries = Object.entries(emitTasks);
1488
+ if (entries.length === 0) {
1489
+ return undefined;
1490
+ }
1491
+ const tasks = entries
1492
+ .map(([rel, content]) => parseTaskSummary(rel, content))
1493
+ .sort((a, b) => a.slug.localeCompare(b.slug));
1494
+ const setSlugs = new Set(tasks.map((t) => t.slug));
1495
+ const lines: string[] = [];
1496
+ const n = tasks.length;
1497
+ lines.push(
1498
+ `Auto-tasked \`${specSlug}\` into ${n} backlog task${n === 1 ? '' : 's'}.`,
1499
+ );
1500
+ lines.push('');
1501
+ lines.push('## Tasks');
1502
+ lines.push('');
1503
+ for (const t of tasks) {
1504
+ lines.push(`- **${t.slug}** — ${t.title}`);
1505
+ if (t.covers.length > 0) {
1506
+ lines.push(` - covers: ${t.covers.map((c) => `US #${c}`).join(', ')}`);
1507
+ }
1508
+ if (t.blockedBy.length > 0) {
1509
+ lines.push(` - blockedBy: ${t.blockedBy.join(', ')}`);
1510
+ }
1511
+ if (t.needsAnswers) {
1512
+ lines.push(' - needsAnswers: true');
1513
+ }
1514
+ }
1515
+ // Dependency graph: keystone(s) are tasks whose blockedBy names no OTHER
1516
+ // member of this set (an external blocker — e.g. a task already in `done/` —
1517
+ // does not prevent this set-relative starting point); the edges list every
1518
+ // set-internal `blockedBy` link so a reviewer can trace the ordering.
1519
+ const keystones = tasks.filter((t) =>
1520
+ t.blockedBy.every((b) => !setSlugs.has(b)),
1521
+ );
1522
+ const edges = tasks.flatMap((t) =>
1523
+ t.blockedBy.filter((b) => setSlugs.has(b)).map((b) => `${t.slug} ← ${b}`),
1524
+ );
1525
+ if (keystones.length > 0 || edges.length > 0) {
1526
+ lines.push('');
1527
+ lines.push('## Dependency graph');
1528
+ lines.push('');
1529
+ if (keystones.length > 0) {
1530
+ const word = keystones.length === 1 ? 'Keystone' : 'Keystones';
1531
+ lines.push(`- ${word}: ${keystones.map((k) => k.slug).join(', ')}`);
1532
+ }
1533
+ for (const edge of edges) {
1534
+ lines.push(`- ${edge}`);
1535
+ }
1536
+ }
1537
+ const uncertain = tasks.filter((t) => t.needsAnswers);
1538
+ if (uncertain.length > 0) {
1539
+ lines.push('');
1540
+ lines.push('## Needs answers');
1541
+ lines.push('');
1542
+ for (const t of uncertain) {
1543
+ lines.push(`- **${t.slug}**:`);
1544
+ if (t.openQuestions.length === 0) {
1545
+ lines.push(' - (no specific questions surfaced)');
1546
+ continue;
1547
+ }
1548
+ for (const q of t.openQuestions) {
1549
+ lines.push(` - ${q}`);
1550
+ }
1551
+ }
1552
+ }
1553
+ return lines.join('\n');
1554
+ }
1555
+
1556
+ /** Summary of one emitted task, for {@link composeTaskingProposeBody}. */
1557
+ interface TaskSummary {
1558
+ slug: string;
1559
+ title: string;
1560
+ covers: string[];
1561
+ blockedBy: string[];
1562
+ needsAnswers: boolean;
1563
+ openQuestions: string[];
1564
+ }
1565
+
1566
+ /**
1567
+ * Parse one emitted task file into a {@link TaskSummary}. Uses
1568
+ * {@link parseFrontmatter} for the shared fields; scans the raw frontmatter
1569
+ * block for `covers:` (not currently modelled on the parser's typed shape) and
1570
+ * the body for a trailing `## Open questions` bullet list (the loop's
1571
+ * uncertain-task disposition sink, see {@link appendQuestionsBlock}).
1572
+ */
1573
+ function parseTaskSummary(rel: string, content: string): TaskSummary {
1574
+ const fm = parseFrontmatter(content);
1575
+ const slug = fm.slug ?? basename(rel).replace(/\.md$/i, '');
1576
+ const title = readTitleField(content) ?? slug;
1577
+ return {
1578
+ slug,
1579
+ title,
1580
+ covers: readCoversField(content),
1581
+ blockedBy: fm.blockedBy,
1582
+ needsAnswers: fm.needsAnswers === true,
1583
+ openQuestions: readOpenQuestions(content),
1584
+ };
1585
+ }
1586
+
1587
+ /**
1588
+ * Extract the `title:` scalar from the raw frontmatter block (not on
1589
+ * {@link parseFrontmatter}'s typed shape). Missing / malformed ⇒ `undefined`.
1590
+ * Comment-tolerant: a trailing `# ...` after an UNQUOTED value is stripped.
1591
+ */
1592
+ function readTitleField(content: string): string | undefined {
1593
+ const normalized = content.replace(/\r\n/g, '\n').replace(/^\uFEFF/, '');
1594
+ if (!normalized.startsWith('---\n')) {
1595
+ return undefined;
1596
+ }
1597
+ const lines = normalized.split('\n');
1598
+ const closing = lines.indexOf('---', 1);
1599
+ if (closing === -1) {
1600
+ return undefined;
1601
+ }
1602
+ for (let i = 1; i < closing; i++) {
1603
+ const m = lines[i].match(/^title:\s*(.*)$/);
1604
+ if (!m) continue;
1605
+ let value = m[1].trim();
1606
+ if (value === '') return undefined;
1607
+ // Quoted ⇒ return inner text verbatim; unquoted ⇒ strip trailing `# ...`.
1608
+ if (
1609
+ (value.startsWith('"') && value.endsWith('"')) ||
1610
+ (value.startsWith("'") && value.endsWith("'"))
1611
+ ) {
1612
+ return value.slice(1, -1);
1613
+ }
1614
+ const hash = value.indexOf('#');
1615
+ if (hash !== -1) value = value.slice(0, hash).trimEnd();
1616
+ return value === '' ? undefined : value;
1617
+ }
1618
+ return undefined;
1619
+ }
1620
+
1621
+ /**
1622
+ * Extract the `covers: [...]` inline-list values from the raw frontmatter block
1623
+ * of a task (not on {@link parseFrontmatter}'s typed shape, so we scan the raw
1624
+ * fence here). Missing / malformed / empty ⇒ `[]`. Comment-tolerant (a trailing
1625
+ * `# ...` after the `]` is ignored, matching the house style on `blockedBy: []
1626
+ * # startable now`).
1627
+ */
1628
+ function readCoversField(content: string): string[] {
1629
+ const normalized = content.replace(/\r\n/g, '\n').replace(/^\uFEFF/, '');
1630
+ if (!normalized.startsWith('---\n')) {
1631
+ return [];
1632
+ }
1633
+ const lines = normalized.split('\n');
1634
+ const closing = lines.indexOf('---', 1);
1635
+ if (closing === -1) {
1636
+ return [];
1637
+ }
1638
+ for (let i = 1; i < closing; i++) {
1639
+ const line = lines[i];
1640
+ const m = line.match(/^covers:\s*\[([^\]]*)\]/);
1641
+ if (!m) continue;
1642
+ return m[1]
1643
+ .split(',')
1644
+ .map((s) => s.trim().replace(/^['"]|['"]$/g, ''))
1645
+ .filter((s) => s !== '');
1646
+ }
1647
+ return [];
1648
+ }
1649
+
1650
+ /**
1651
+ * Extract the bullets under a trailing `## Open questions` heading in a task
1652
+ * body — the tasker review loop's uncertain-task sink (see
1653
+ * {@link appendQuestionsBlock}). Returns `[]` when no such block exists.
1654
+ */
1655
+ function readOpenQuestions(content: string): string[] {
1656
+ const normalized = content.replace(/\r\n/g, '\n');
1657
+ const idx = normalized.lastIndexOf(`\n${OPEN_QUESTIONS_HEADING}`);
1658
+ if (idx === -1) {
1659
+ return [];
1660
+ }
1661
+ const tail = normalized.slice(idx + 1);
1662
+ const lines = tail.split('\n').slice(1);
1663
+ const questions: string[] = [];
1664
+ for (const raw of lines) {
1665
+ const line = raw.trimEnd();
1666
+ if (line === '') continue;
1667
+ const m = line.match(/^- (.*)$/);
1668
+ if (!m) break;
1669
+ questions.push(m[1]);
1670
+ }
1671
+ return questions;
1672
+ }
1673
+
1430
1674
  /** List `*.md` files in `dir`, sorted; an absent dir reads as empty. */
1431
1675
  function listMarkdown(dir: string): string[] {
1432
1676
  let entries: string[];
@@ -10,6 +10,7 @@ import {
10
10
  import {resolveSidecarIdentity, sidecarPathFor} from './sidecar.js';
11
11
  import type {TriageAutoKind} from './triage-gate.js';
12
12
  import {renderTaskBody, renderSpecBody} from './buildable-body.js';
13
+ import {setFrontmatterMarker} from './frontmatter.js';
13
14
 
14
15
  /**
15
16
  * The engine-owned TRIAGE PERSIST (spec `advance-loop`, task `advance-rung-triage`,
@@ -233,7 +234,7 @@ export interface PromoteObservationOptions {
233
234
  * CAS) — NOT intake's branch+integrate band — so the CAS-loser-backs-off
234
235
  * guarantee and the same-commit note deletion are uniform across the two routes.
235
236
  * MIGRATE step (spec `prd-to-spec-vocabulary-cutover-and-migration-command`): the
236
- * produced VALUE is `'spec'` (the verdict outcome renamed from `'prd'`).
237
+ * produced VALUE is `'spec'` (the verdict outcome renamed from ''prd'').
237
238
  */
238
239
  artifact?: 'task' | 'spec';
239
240
  /**
@@ -273,11 +274,20 @@ export interface PromoteObservationOptions {
273
274
  export interface PromoteObservationResult {
274
275
  /**
275
276
  * `promoted` (the new item landed via the CAS + the observation+sidecar were
276
- * deleted in the SAME commit), `lost` (the same-slug new-item race was lost —
277
- * the loser backs off, observation left INTACT), or `contended` (the CAS push
278
- * kept failing). Maps onto the claim-CAS exit codes.
277
+ * deleted in the SAME commit), `already-triaged` (the target task ALREADY exists
278
+ * on the arbiter AND was PROVABLY minted from THIS observation in a prior run a
279
+ * TERMINAL, benign idempotency fact, exit 0; the observation is left intact but a
280
+ * retry can never succeed, so it is NOT loud), `lost` (a genuine concurrent-create
281
+ * race for a same-path NEW item was lost — the loser backs off, observation left
282
+ * INTACT, retry helps), or `contended` (the CAS push kept failing). Maps onto the
283
+ * claim-CAS exit codes.
279
284
  */
280
- outcome: 'promoted' | 'lost' | 'contended' | 'usage-error';
285
+ outcome:
286
+ | 'promoted'
287
+ | 'already-triaged'
288
+ | 'lost'
289
+ | 'contended'
290
+ | 'usage-error';
281
291
  exitCode: 0 | 1 | 2 | 3;
282
292
  /** The new task's path (relative to repo root) on a WIN. */
283
293
  newItemPath?: string;
@@ -293,7 +303,7 @@ export interface PromoteObservationResult {
293
303
  * (create + delete).
294
304
  *
295
305
  * The artifact TYPE (`options.artifact`) selects the target + body shape: `'task'`
296
- * (default) mints `work/tasks/ready/<slug>.md`; `'prd'` mints
306
+ * (default) mints `work/tasks/ready/<slug>.md`; `'spec'` mints
297
307
  * `work/specs/proposed/<slug>.md` (SPEC staging — a human later promotes it to
298
308
  * `ready/`). BOTH routes use the SAME triage-local {@link createItemThroughCas}
299
309
  * writer (NOT intake's `switchToWorkBranch`/`performIntegration` branch+integrate
@@ -340,11 +350,24 @@ export async function promoteObservation(
340
350
  `${newSlug}.md`,
341
351
  );
342
352
  const by = options.by || resolveBy(cwd, env);
343
- const content = ensureTaskDispatchable(
344
- artifact,
345
- newSlug,
346
- options.stubContent ??
347
- buildPromotedBody(artifact, newSlug, readItem(cwd, itemPath, env)),
353
+ // Stamp the observation→task PROVENANCE back-reference `promotedFrom: <item>` into
354
+ // the minted body (task `observation-triage-already-triaged-benign-skip`). It is
355
+ // the PROVABLE LINK the create-CAS lost-race disambiguator reads to tell case A
356
+ // (this observation ALREADY minted this task — already triaged, benign skip) from
357
+ // case B (a genuine concurrent-create race with an UNRELATED same-path item — loud
358
+ // exit 2). Distinct from `origin:` (how BORN: human/issue) and `reviewOf:` (the
359
+ // review-nits back-pointer); this is the triage-promote lineage. Matching on this
360
+ // explicit field — not the slug — is what keeps an unrelated same-slug task from
361
+ // false-positiving into a benign skip.
362
+ const content = setFrontmatterMarker(
363
+ ensureTaskDispatchable(
364
+ artifact,
365
+ newSlug,
366
+ options.stubContent ??
367
+ buildPromotedBody(artifact, newSlug, readItem(cwd, itemPath, env)),
368
+ ),
369
+ 'promotedFrom',
370
+ item,
348
371
  );
349
372
 
350
373
  // The note + its answered sidecar `git rm` IN THE SAME create commit (promote =
@@ -359,6 +382,10 @@ export async function promoteObservation(
359
382
  path: newItemPath,
360
383
  content,
361
384
  deletePaths: [itemPath, sidecarPath],
385
+ // The provenance disambiguator: on a LOST create-race, the CAS reads the
386
+ // existing file's `promotedFrom:` and, iff it equals THIS observation, reports
387
+ // the benign `already-exists-from-source` instead of the loud `lost`.
388
+ sourceItem: item,
362
389
  cwd,
363
390
  arbiter: options.arbiter,
364
391
  by,
@@ -369,6 +396,23 @@ export async function promoteObservation(
369
396
  ? {contention: options.contention}
370
397
  : {}),
371
398
  });
399
+ // TERMINAL 'already triaged' (case A): the task this observation would mint
400
+ // ALREADY exists on the arbiter AND was provably minted from THIS observation in
401
+ // a prior run. A benign idempotency fact (exit 0) — the human already decided when
402
+ // they minted it — NOT a retry. The observation is left intact (a later
403
+ // discharge-by-deletion or a human deletes it); this leg does not red CI.
404
+ if (created.outcome === 'already-exists-from-source') {
405
+ const message =
406
+ `promote ${item}: ${newItemPath} was already minted FROM this observation ` +
407
+ `in a prior run (${created.message}) — already triaged. Benign skip, no retry.`;
408
+ note(message);
409
+ return {
410
+ outcome: 'already-triaged',
411
+ exitCode: 0,
412
+ newItemPath,
413
+ message,
414
+ };
415
+ }
372
416
  if (created.exitCode !== 0) {
373
417
  const message =
374
418
  `promote ${item}: the new item ${newItemPath} ${created.outcome} the ` +
@@ -398,7 +442,7 @@ export async function promoteObservation(
398
442
  *
399
443
  * The artifact TYPE selects the frontmatter + lead heading: a `task` gets
400
444
  * `## What to build` + `blockedBy: []` + a `## Prompt` (the buildable-task shape);
401
- * a `prd` gets `## Problem Statement` (the SPEC document shape, with no `blockedBy` —
445
+ * a `spec` gets `## Problem Statement` (the SPEC document shape, with no `blockedBy` —
402
446
  * a SPEC is not a blockable task — and no `## Prompt`, since a SPEC is not
403
447
  * dispatched by `do`/`run`). BOTH carry the SAME transcribed mechanism prose +
404
448
  * open-question block, so a SPEC minted into `proposed/` is just as self-contained
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present weth, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ # Vendored: `incur/src/internal/agents.ts` (MIT)
2
+
3
+ `agents.ts` here is a **verbatim** copy of `src/internal/agents.ts` from
4
+ [wevm/incur](https://github.com/wevm/incur) (MIT). See `LICENSE` beside it for the
5
+ required copyright + permission notice, which is also duplicated as a header
6
+ comment at the top of `agents.ts` itself so the file stays attribution-complete
7
+ in isolation.
8
+
9
+ We VENDOR rather than depend (see
10
+ `docs/adr/skill-install-vendors-incur-agents-map.md`): the harness-destination
11
+ map + copy/symlink logic are the one hard, drift-prone piece we need, and the
12
+ file is dependency-free (only `node:fs`/`os`/`path`). Keep it BYTE-CLOSE to
13
+ upstream so a future incur update is a mechanical re-copy — put all wrapper
14
+ code outside this directory (`../../install-skills.ts`).
15
+
16
+ To refresh: re-fetch `src/internal/agents.ts` from the upstream tag you want,
17
+ overwrite `agents.ts`, keep the small ATTRIBUTION HEADER block at the top
18
+ (above the original `import` lines) intact, and re-copy `LICENSE` if it
19
+ changed upstream.