@pennixrv/trellis 0.7.1 → 0.7.2-beta.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 (318) hide show
  1. package/README.md +10 -7
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +6 -4
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/ablate.d.ts.map +1 -1
  6. package/dist/commands/ablate.js +2 -0
  7. package/dist/commands/ablate.js.map +1 -1
  8. package/dist/commands/backup.d.ts +4 -0
  9. package/dist/commands/backup.d.ts.map +1 -0
  10. package/dist/commands/backup.js +75 -0
  11. package/dist/commands/backup.js.map +1 -0
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +705 -681
  14. package/dist/commands/init.js.map +1 -1
  15. package/dist/commands/mem.d.ts.map +1 -1
  16. package/dist/commands/mem.js +49 -7
  17. package/dist/commands/mem.js.map +1 -1
  18. package/dist/commands/task-progress.d.ts +1 -0
  19. package/dist/commands/task-progress.d.ts.map +1 -1
  20. package/dist/commands/task-progress.js +67 -25
  21. package/dist/commands/task-progress.js.map +1 -1
  22. package/dist/commands/teamwork/adapters/acp.d.ts +20 -0
  23. package/dist/commands/teamwork/adapters/acp.d.ts.map +1 -0
  24. package/dist/commands/teamwork/adapters/acp.js +268 -0
  25. package/dist/commands/teamwork/adapters/acp.js.map +1 -0
  26. package/dist/commands/teamwork/adapters/claude.d.ts +1 -0
  27. package/dist/commands/teamwork/adapters/claude.d.ts.map +1 -1
  28. package/dist/commands/teamwork/adapters/claude.js +5 -0
  29. package/dist/commands/teamwork/adapters/claude.js.map +1 -1
  30. package/dist/commands/teamwork/adapters/codex.d.ts +3 -2
  31. package/dist/commands/teamwork/adapters/codex.d.ts.map +1 -1
  32. package/dist/commands/teamwork/adapters/codex.js +14 -15
  33. package/dist/commands/teamwork/adapters/codex.js.map +1 -1
  34. package/dist/commands/teamwork/adapters/index.d.ts +23 -1
  35. package/dist/commands/teamwork/adapters/index.d.ts.map +1 -1
  36. package/dist/commands/teamwork/adapters/index.js +163 -24
  37. package/dist/commands/teamwork/adapters/index.js.map +1 -1
  38. package/dist/commands/teamwork/agent-loader.d.ts +2 -1
  39. package/dist/commands/teamwork/agent-loader.d.ts.map +1 -1
  40. package/dist/commands/teamwork/agent-loader.js +2 -1
  41. package/dist/commands/teamwork/agent-loader.js.map +1 -1
  42. package/dist/commands/teamwork/artifact.d.ts +33 -10
  43. package/dist/commands/teamwork/artifact.d.ts.map +1 -1
  44. package/dist/commands/teamwork/artifact.js +182 -50
  45. package/dist/commands/teamwork/artifact.js.map +1 -1
  46. package/dist/commands/teamwork/batch-reservation.d.ts +16 -0
  47. package/dist/commands/teamwork/batch-reservation.d.ts.map +1 -0
  48. package/dist/commands/teamwork/batch-reservation.js +43 -0
  49. package/dist/commands/teamwork/batch-reservation.js.map +1 -0
  50. package/dist/commands/teamwork/batch.d.ts +22 -0
  51. package/dist/commands/teamwork/batch.d.ts.map +1 -0
  52. package/dist/commands/teamwork/batch.js +394 -0
  53. package/dist/commands/teamwork/batch.js.map +1 -0
  54. package/dist/commands/teamwork/context-loader.d.ts +1 -1
  55. package/dist/commands/teamwork/context-loader.d.ts.map +1 -1
  56. package/dist/commands/teamwork/context-loader.js +19 -6
  57. package/dist/commands/teamwork/context-loader.js.map +1 -1
  58. package/dist/commands/teamwork/coordination.d.ts +88 -0
  59. package/dist/commands/teamwork/coordination.d.ts.map +1 -0
  60. package/dist/commands/teamwork/coordination.js +153 -0
  61. package/dist/commands/teamwork/coordination.js.map +1 -0
  62. package/dist/commands/teamwork/coordinator.d.ts +17 -3
  63. package/dist/commands/teamwork/coordinator.d.ts.map +1 -1
  64. package/dist/commands/teamwork/coordinator.js +772 -289
  65. package/dist/commands/teamwork/coordinator.js.map +1 -1
  66. package/dist/commands/teamwork/create.d.ts +1 -0
  67. package/dist/commands/teamwork/create.d.ts.map +1 -1
  68. package/dist/commands/teamwork/create.js +2 -1
  69. package/dist/commands/teamwork/create.js.map +1 -1
  70. package/dist/commands/teamwork/dev-parse-trace.d.ts +2 -1
  71. package/dist/commands/teamwork/dev-parse-trace.d.ts.map +1 -1
  72. package/dist/commands/teamwork/dev-parse-trace.js +2 -0
  73. package/dist/commands/teamwork/dev-parse-trace.js.map +1 -1
  74. package/dist/commands/teamwork/foreground-wait.d.ts +6 -0
  75. package/dist/commands/teamwork/foreground-wait.d.ts.map +1 -0
  76. package/dist/commands/teamwork/foreground-wait.js +65 -0
  77. package/dist/commands/teamwork/foreground-wait.js.map +1 -0
  78. package/dist/commands/teamwork/guard.d.ts +2 -2
  79. package/dist/commands/teamwork/guard.d.ts.map +1 -1
  80. package/dist/commands/teamwork/guard.js +23 -5
  81. package/dist/commands/teamwork/guard.js.map +1 -1
  82. package/dist/commands/teamwork/index.d.ts.map +1 -1
  83. package/dist/commands/teamwork/index.js +139 -10
  84. package/dist/commands/teamwork/index.js.map +1 -1
  85. package/dist/commands/teamwork/kill.d.ts +2 -0
  86. package/dist/commands/teamwork/kill.d.ts.map +1 -1
  87. package/dist/commands/teamwork/kill.js +79 -38
  88. package/dist/commands/teamwork/kill.js.map +1 -1
  89. package/dist/commands/teamwork/messages.d.ts.map +1 -1
  90. package/dist/commands/teamwork/messages.js +3 -0
  91. package/dist/commands/teamwork/messages.js.map +1 -1
  92. package/dist/commands/teamwork/plan.d.ts +22 -0
  93. package/dist/commands/teamwork/plan.d.ts.map +1 -0
  94. package/dist/commands/teamwork/plan.js +288 -0
  95. package/dist/commands/teamwork/plan.js.map +1 -0
  96. package/dist/commands/teamwork/process-identity.d.ts +3 -0
  97. package/dist/commands/teamwork/process-identity.d.ts.map +1 -0
  98. package/dist/commands/teamwork/process-identity.js +24 -0
  99. package/dist/commands/teamwork/process-identity.js.map +1 -0
  100. package/dist/commands/teamwork/profiles.d.ts +43 -6
  101. package/dist/commands/teamwork/profiles.d.ts.map +1 -1
  102. package/dist/commands/teamwork/profiles.js +138 -110
  103. package/dist/commands/teamwork/profiles.js.map +1 -1
  104. package/dist/commands/teamwork/prompt.d.ts +4 -0
  105. package/dist/commands/teamwork/prompt.d.ts.map +1 -0
  106. package/dist/commands/teamwork/prompt.js +33 -0
  107. package/dist/commands/teamwork/prompt.js.map +1 -0
  108. package/dist/commands/teamwork/result.d.ts +54 -0
  109. package/dist/commands/teamwork/result.d.ts.map +1 -0
  110. package/dist/commands/teamwork/result.js +94 -0
  111. package/dist/commands/teamwork/result.js.map +1 -0
  112. package/dist/commands/teamwork/rm.d.ts.map +1 -1
  113. package/dist/commands/teamwork/rm.js +64 -64
  114. package/dist/commands/teamwork/rm.js.map +1 -1
  115. package/dist/commands/teamwork/run.d.ts +5 -0
  116. package/dist/commands/teamwork/run.d.ts.map +1 -1
  117. package/dist/commands/teamwork/run.js +120 -24
  118. package/dist/commands/teamwork/run.js.map +1 -1
  119. package/dist/commands/teamwork/runtime.d.ts +9 -0
  120. package/dist/commands/teamwork/runtime.d.ts.map +1 -0
  121. package/dist/commands/teamwork/runtime.js +372 -0
  122. package/dist/commands/teamwork/runtime.js.map +1 -0
  123. package/dist/commands/teamwork/send.d.ts +1 -0
  124. package/dist/commands/teamwork/send.d.ts.map +1 -1
  125. package/dist/commands/teamwork/send.js +2 -1
  126. package/dist/commands/teamwork/send.js.map +1 -1
  127. package/dist/commands/teamwork/spawn.d.ts +10 -3
  128. package/dist/commands/teamwork/spawn.d.ts.map +1 -1
  129. package/dist/commands/teamwork/spawn.js +42 -27
  130. package/dist/commands/teamwork/spawn.js.map +1 -1
  131. package/dist/commands/teamwork/store/lock.js +28 -4
  132. package/dist/commands/teamwork/store/lock.js.map +1 -1
  133. package/dist/commands/teamwork/supervisor/inbox.d.ts +1 -0
  134. package/dist/commands/teamwork/supervisor/inbox.d.ts.map +1 -1
  135. package/dist/commands/teamwork/supervisor/inbox.js +28 -9
  136. package/dist/commands/teamwork/supervisor/inbox.js.map +1 -1
  137. package/dist/commands/teamwork/supervisor/stdout.d.ts.map +1 -1
  138. package/dist/commands/teamwork/supervisor/stdout.js +2 -3
  139. package/dist/commands/teamwork/supervisor/stdout.js.map +1 -1
  140. package/dist/commands/teamwork/supervisor/warning.d.ts.map +1 -1
  141. package/dist/commands/teamwork/supervisor/warning.js +2 -3
  142. package/dist/commands/teamwork/supervisor/warning.js.map +1 -1
  143. package/dist/commands/teamwork/supervisor.d.ts +5 -0
  144. package/dist/commands/teamwork/supervisor.d.ts.map +1 -1
  145. package/dist/commands/teamwork/supervisor.js +21 -12
  146. package/dist/commands/teamwork/supervisor.js.map +1 -1
  147. package/dist/commands/teamwork/teammate-adapters.d.ts +0 -5
  148. package/dist/commands/teamwork/teammate-adapters.d.ts.map +1 -1
  149. package/dist/commands/teamwork/teammate-adapters.js +9 -56
  150. package/dist/commands/teamwork/teammate-adapters.js.map +1 -1
  151. package/dist/commands/teamwork/teammate-config.d.ts +5 -1
  152. package/dist/commands/teamwork/teammate-config.d.ts.map +1 -1
  153. package/dist/commands/teamwork/teammate-config.js +62 -19
  154. package/dist/commands/teamwork/teammate-config.js.map +1 -1
  155. package/dist/commands/teamwork/teammate-launch.d.ts +15 -6
  156. package/dist/commands/teamwork/teammate-launch.d.ts.map +1 -1
  157. package/dist/commands/teamwork/teammate-launch.js +129 -22
  158. package/dist/commands/teamwork/teammate-launch.js.map +1 -1
  159. package/dist/commands/teamwork/wait.d.ts +2 -0
  160. package/dist/commands/teamwork/wait.d.ts.map +1 -1
  161. package/dist/commands/teamwork/wait.js +44 -3
  162. package/dist/commands/teamwork/wait.js.map +1 -1
  163. package/dist/commands/uninstall.d.ts.map +1 -1
  164. package/dist/commands/uninstall.js +92 -84
  165. package/dist/commands/uninstall.js.map +1 -1
  166. package/dist/commands/update.d.ts.map +1 -1
  167. package/dist/commands/update.js +635 -592
  168. package/dist/commands/update.js.map +1 -1
  169. package/dist/commands/workflow.d.ts.map +1 -1
  170. package/dist/commands/workflow.js +94 -89
  171. package/dist/commands/workflow.js.map +1 -1
  172. package/dist/configurators/shared.js +2 -2
  173. package/dist/configurators/shared.js.map +1 -1
  174. package/dist/configurators/workflow.d.ts.map +1 -1
  175. package/dist/configurators/workflow.js +14 -32
  176. package/dist/configurators/workflow.js.map +1 -1
  177. package/dist/constants/paths.d.ts +3 -10
  178. package/dist/constants/paths.d.ts.map +1 -1
  179. package/dist/constants/paths.js +4 -13
  180. package/dist/constants/paths.js.map +1 -1
  181. package/dist/migrations/manifests/0.7.1-beta.0.json +9 -0
  182. package/dist/migrations/manifests/0.7.1-beta.1.json +186 -0
  183. package/dist/migrations/manifests/0.7.1-beta.10.json +9 -0
  184. package/dist/migrations/manifests/0.7.1-beta.11.json +9 -0
  185. package/dist/migrations/manifests/0.7.1-beta.12.json +9 -0
  186. package/dist/migrations/manifests/0.7.1-beta.13.json +9 -0
  187. package/dist/migrations/manifests/0.7.1-beta.14.json +11 -0
  188. package/dist/migrations/manifests/0.7.1-beta.15.json +9 -0
  189. package/dist/migrations/manifests/0.7.1-beta.16.json +9 -0
  190. package/dist/migrations/manifests/0.7.1-beta.17.json +9 -0
  191. package/dist/migrations/manifests/0.7.1-beta.18.json +9 -0
  192. package/dist/migrations/manifests/0.7.1-beta.19.json +9 -0
  193. package/dist/migrations/manifests/0.7.1-beta.2.json +522 -0
  194. package/dist/migrations/manifests/0.7.1-beta.3.json +9 -0
  195. package/dist/migrations/manifests/0.7.1-beta.4.json +9 -0
  196. package/dist/migrations/manifests/0.7.1-beta.5.json +9 -0
  197. package/dist/migrations/manifests/0.7.1-beta.6.json +10 -0
  198. package/dist/migrations/manifests/0.7.1-beta.7.json +8 -0
  199. package/dist/migrations/manifests/0.7.1-beta.8.json +481 -0
  200. package/dist/migrations/manifests/0.7.1-beta.9.json +191 -0
  201. package/dist/migrations/manifests/0.7.2-beta.0.json +11 -0
  202. package/dist/templates/claude/hooks/statusline.py +9 -4
  203. package/dist/templates/codex/hooks/session-start.py +14 -11
  204. package/dist/templates/codex/hooks.json +10 -0
  205. package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/workflow.md +2 -0
  206. package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/workflow.md +2 -0
  207. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +1 -1
  208. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +1 -1
  209. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +5 -5
  210. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +3 -2
  211. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +1 -1
  212. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +1 -1
  213. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-teamwork.md +14 -2
  214. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -2
  215. package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +2 -1
  216. package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +2 -2
  217. package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/workflow.md +8 -0
  218. package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/mcp-setup.md +4 -2
  219. package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-task-planning.md +2 -2
  220. package/dist/templates/common/bundled-skills/trellis-task-implementation/workflow.md +6 -0
  221. package/dist/templates/common/bundled-skills/trellis-teamwork/SKILL.md +18 -3
  222. package/dist/templates/common/bundled-skills/trellis-teamwork/references/coordination-modes.md +34 -3
  223. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-configuration-matrix.md +37 -26
  224. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +128 -14
  225. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +152 -20
  226. package/dist/templates/common/commands/start.md +2 -2
  227. package/dist/templates/common/skills/before-dev.md +1 -4
  228. package/dist/templates/common/skills/break-loop.md +7 -18
  229. package/dist/templates/common/skills/check.md +7 -11
  230. package/dist/templates/common/skills/{brainstorm.md → request-intake.md} +5 -3
  231. package/dist/templates/common/skills/update-spec.md +5 -7
  232. package/dist/templates/copilot/hooks/session-start.py +14 -11
  233. package/dist/templates/copilot/index.d.ts +1 -6
  234. package/dist/templates/copilot/index.d.ts.map +1 -1
  235. package/dist/templates/copilot/index.js +1 -14
  236. package/dist/templates/copilot/index.js.map +1 -1
  237. package/dist/templates/extract.js +4 -0
  238. package/dist/templates/extract.js.map +1 -1
  239. package/dist/templates/markdown/spec/backend/index.md.txt +4 -35
  240. package/dist/templates/markdown/spec/frontend/index.md.txt +4 -36
  241. package/dist/templates/omp/extensions/trellis/index.ts.txt +2 -2
  242. package/dist/templates/opencode/lib/session-utils.js +301 -209
  243. package/dist/templates/pi/extensions/trellis/index.ts.txt +17 -1
  244. package/dist/templates/shared-hooks/guard-teamwork-wait.py +53 -0
  245. package/dist/templates/shared-hooks/index.d.ts +1 -1
  246. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  247. package/dist/templates/shared-hooks/index.js +1 -0
  248. package/dist/templates/shared-hooks/index.js.map +1 -1
  249. package/dist/templates/shared-hooks/inject-workflow-state.py +8 -3
  250. package/dist/templates/shared-hooks/session-start.py +16 -12
  251. package/dist/templates/snow/SNOW.md +1 -1
  252. package/dist/templates/trellis/agents/delegated-worktree-primary-agent.md +9 -0
  253. package/dist/templates/trellis/agents/profiles/architecture.md +9 -0
  254. package/dist/templates/trellis/agents/profiles/diagnosis.md +9 -0
  255. package/dist/templates/trellis/agents/profiles/exploration.md +9 -0
  256. package/dist/templates/trellis/agents/profiles/research.md +9 -0
  257. package/dist/templates/trellis/agents/profiles/review.md +9 -0
  258. package/dist/templates/trellis/agents/profiles/verification.md +9 -0
  259. package/dist/templates/trellis/agents/teammate-profiles.json +56 -4
  260. package/dist/templates/trellis/agents/teammate.md +4 -52
  261. package/dist/templates/trellis/config.yaml +2 -5
  262. package/dist/templates/trellis/index.d.ts +4 -0
  263. package/dist/templates/trellis/index.d.ts.map +1 -1
  264. package/dist/templates/trellis/index.js +22 -4
  265. package/dist/templates/trellis/index.js.map +1 -1
  266. package/dist/templates/trellis/scripts/add_session.py +1 -1
  267. package/dist/templates/trellis/scripts/common/active_task.py +3 -47
  268. package/dist/templates/trellis/scripts/common/paths.py +9 -25
  269. package/dist/templates/trellis/scripts/common/safe_commit.py +8 -8
  270. package/dist/templates/trellis/scripts/common/task_context.py +11 -51
  271. package/dist/templates/trellis/scripts/common/task_continuity.py +129 -60
  272. package/dist/templates/trellis/scripts/common/task_layout.py +80 -0
  273. package/dist/templates/trellis/scripts/common/task_move.py +345 -0
  274. package/dist/templates/trellis/scripts/common/task_store.py +35 -57
  275. package/dist/templates/trellis/scripts/common/task_utils.py +23 -19
  276. package/dist/templates/trellis/scripts/common/task_workflow.py +339 -46
  277. package/dist/templates/trellis/scripts/common/tasks.py +6 -5
  278. package/dist/templates/trellis/scripts/hooks/linear_sync.py +13 -3
  279. package/dist/templates/trellis/scripts/task.py +71 -24
  280. package/dist/templates/trellis/teamwork.json +10 -0
  281. package/dist/templates/trellis/workflow.md +11 -2
  282. package/dist/utils/ablation-store.d.ts +1 -1
  283. package/dist/utils/ablation-store.d.ts.map +1 -1
  284. package/dist/utils/ablation-store.js +58 -24
  285. package/dist/utils/ablation-store.js.map +1 -1
  286. package/dist/utils/asset-backup.d.ts +138 -0
  287. package/dist/utils/asset-backup.d.ts.map +1 -0
  288. package/dist/utils/asset-backup.js +513 -0
  289. package/dist/utils/asset-backup.js.map +1 -0
  290. package/dist/utils/atomic-write.d.ts +1 -1
  291. package/dist/utils/atomic-write.d.ts.map +1 -1
  292. package/dist/utils/atomic-write.js +6 -3
  293. package/dist/utils/atomic-write.js.map +1 -1
  294. package/dist/utils/manifest-prune.d.ts +4 -1
  295. package/dist/utils/manifest-prune.d.ts.map +1 -1
  296. package/dist/utils/manifest-prune.js +17 -6
  297. package/dist/utils/manifest-prune.js.map +1 -1
  298. package/dist/utils/template-fetcher.d.ts +4 -19
  299. package/dist/utils/template-fetcher.d.ts.map +1 -1
  300. package/dist/utils/template-fetcher.js +9 -45
  301. package/dist/utils/template-fetcher.js.map +1 -1
  302. package/dist/utils/template-hash.d.ts +5 -4
  303. package/dist/utils/template-hash.d.ts.map +1 -1
  304. package/dist/utils/template-hash.js +24 -17
  305. package/dist/utils/template-hash.js.map +1 -1
  306. package/package.json +3 -2
  307. package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
  308. package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -13
  309. package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
  310. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -170
  311. package/dist/templates/copilot/prompts/check.prompt.md +0 -110
  312. package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
  313. package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -22
  314. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
  315. package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
  316. package/dist/templates/copilot/prompts/record-session.prompt.md +0 -58
  317. package/dist/templates/copilot/prompts/start.prompt.md +0 -65
  318. package/dist/templates/copilot/prompts/update-spec.prompt.md +0 -358
@@ -19,6 +19,7 @@ from pathlib import Path
19
19
  from .io import describe_json_read_failure, read_json_checked
20
20
  from .paths import FILE_TASK_JSON
21
21
  from .types import TaskInfo
22
+ from .task_layout import iter_task_dirs, task_status_dir
22
23
 
23
24
 
24
25
  def load_task(task_dir: Path) -> TaskInfo | None:
@@ -72,14 +73,14 @@ def iter_active_tasks(tasks_dir: Path) -> Iterator[TaskInfo]:
72
73
  Yields:
73
74
  TaskInfo for each valid task.
74
75
  """
75
- if not tasks_dir.is_dir():
76
- return
76
+ from .task_move import require_settled_layout
77
77
 
78
- for d in sorted(tasks_dir.iterdir()):
79
- if not d.is_dir():
80
- continue
78
+ require_settled_layout(tasks_dir.parent.parent)
79
+ for d in iter_task_dirs(tasks_dir):
81
80
  info = load_task(d)
82
81
  if info is not None:
82
+ if info.status != task_status_dir(d, tasks_dir):
83
+ print(f"[WARN] Task status/directory mismatch: {d}", file=sys.stderr)
83
84
  yield info
84
85
 
85
86
 
@@ -9,7 +9,7 @@ Usage (called automatically by task.py hooks):
9
9
  python3 .trellis/scripts/hooks/linear_sync.py complete
10
10
 
11
11
  Manual usage:
12
- TASK_JSON_PATH=.trellis/tasks/<name>/task.json python3 .trellis/scripts/hooks/linear_sync.py sync
12
+ TASK_JSON_PATH=.trellis/tasks/<task-dir>/task.json python3 .trellis/scripts/hooks/linear_sync.py sync
13
13
 
14
14
  Environment:
15
15
  TASK_JSON_PATH - Absolute path to task.json (set by task.py)
@@ -211,8 +211,18 @@ def _resolve_parent_linear_issue(task: dict) -> str | None:
211
211
  return None
212
212
 
213
213
  current_task_dir = Path(task_json_path).parent
214
- tasks_dir = current_task_dir.parent
215
- parent_json = tasks_dir / parent_name / "task.json"
214
+ scripts_dir = Path(__file__).resolve().parents[1]
215
+ if str(scripts_dir) not in sys.path:
216
+ sys.path.insert(0, str(scripts_dir))
217
+ from common.paths import get_tasks_dir
218
+ from common.task_utils import find_task_by_name
219
+ workflow_dir = next((parent for parent in current_task_dir.parents if parent.name == ".trellis"), None)
220
+ if workflow_dir is None:
221
+ return None
222
+ parent_dir = find_task_by_name(parent_name, get_tasks_dir(workflow_dir.parent))
223
+ if parent_dir is None:
224
+ return None
225
+ parent_json = parent_dir / "task.json"
216
226
 
217
227
  if parent_json.exists():
218
228
  try:
@@ -14,6 +14,7 @@ Usage:
14
14
  python3 task.py workflow <id>|--clear # Set/clear per-task workflow selection
15
15
  python3 task.py phase <action> <task-dir> # Read/update task phase workflow state
16
16
  python3 task.py cancel <task-dir> --reason <text> --evidence-ref <ref> # Cancel task lifecycle
17
+ python3 task.py reopen <task-dir> --reason <text> --evidence-ref <ref> # Restore terminal task to planning
17
18
  python3 task.py set-branch <dir> <branch> # Set git branch
18
19
  python3 task.py set-base-branch <dir> <branch> # Set PR target branch
19
20
  python3 task.py set-scope <dir> <scope> # Set scope for PR title
@@ -79,8 +80,11 @@ from common.task_context import (
79
80
  from common.task_workflow import (
80
81
  TaskWorkflowError,
81
82
  cmd_cancel_task,
83
+ cmd_reopen_task,
82
84
  cmd_complete_task,
83
85
  cmd_delegate_start,
86
+ cmd_delegate_bind,
87
+ cmd_delegate_reconcile,
84
88
  cmd_phase,
85
89
  implementation_start_ready,
86
90
  )
@@ -101,7 +105,7 @@ def _record_start_state(
101
105
  task_json_path: Path,
102
106
  repo_root: Path,
103
107
  label: str = "",
104
- ) -> None:
108
+ ) -> Path:
105
109
  """Enter implementation and record its branch after current authorization.
106
110
 
107
111
  Planning and analysis tasks keep their lifecycle status while the active
@@ -121,7 +125,7 @@ def _record_start_state(
121
125
  file=sys.stderr,
122
126
  )
123
127
  print(hint, file=sys.stderr)
124
- return
128
+ return task_json_path
125
129
 
126
130
  applied: list[str] = []
127
131
 
@@ -132,7 +136,7 @@ def _record_start_state(
132
136
  colored(f"Warning: Implementation start withheld; task.json not updated: {error.code}: {error}", Colors.YELLOW),
133
137
  file=sys.stderr,
134
138
  )
135
- return
139
+ return task_json_path
136
140
  if data.get("status") == "planning" and implementation_ready:
137
141
  data["status"] = "in_progress"
138
142
  applied.append(f"✓ Status: planning → in_progress{label}")
@@ -158,15 +162,18 @@ def _record_start_state(
158
162
  )
159
163
 
160
164
  if not applied:
161
- return
165
+ return task_json_path
162
166
 
163
167
  try:
164
168
  assert_task_mutation_allowed(repo_root, task_json_path.parent, data)
165
169
  except TaskWorkflowError as error:
166
170
  print(colored(f"{error.code}: task metadata update withheld: {error}", Colors.YELLOW), file=sys.stderr)
167
- return
171
+ return task_json_path
168
172
 
169
- if not write_json(task_json_path, data):
173
+ from common.task_move import relocate_task
174
+ relocated = relocate_task(repo_root, task_json_path.parent, data)
175
+ task_json_path = relocated / FILE_TASK_JSON
176
+ if not task_json_path.is_file():
170
177
  print(
171
178
  colored(
172
179
  f"Warning: Failed to write {task_json_path}; "
@@ -175,7 +182,7 @@ def _record_start_state(
175
182
  ),
176
183
  file=sys.stderr,
177
184
  )
178
- return
185
+ return task_json_path
179
186
 
180
187
  for line in applied:
181
188
  print(colored(line, Colors.GREEN))
@@ -190,12 +197,15 @@ def _record_start_state(
190
197
  ),
191
198
  file=sys.stderr,
192
199
  )
200
+
193
201
  print(
194
202
  f"Once you branch off, run: python3 {DIR_WORKFLOW}/scripts/task.py "
195
203
  "set-branch <task> <feature-branch>",
196
204
  file=sys.stderr,
197
205
  )
198
206
 
207
+ return task_json_path
208
+
199
209
 
200
210
  def cmd_start(args: argparse.Namespace) -> int:
201
211
  """Set active task."""
@@ -256,18 +266,17 @@ def cmd_start(args: argparse.Namespace) -> int:
256
266
 
257
267
  # Enter implementation only when the accepted phase contract authorizes it.
258
268
  if task_json_path.is_file():
259
- _record_start_state(task_json_path, repo_root, " (degraded)")
269
+ task_json_path = _record_start_state(task_json_path, repo_root, " (degraded)")
260
270
  run_task_hooks("after_start", task_json_path, repo_root)
261
271
  return 0
262
272
 
273
+ if task_json_path.is_file():
274
+ task_json_path = _record_start_state(task_json_path, repo_root)
275
+ task_dir = task_json_path.parent.relative_to(repo_root).as_posix()
263
276
  active = set_active_task(task_dir, repo_root)
264
277
  if active:
265
- print(colored(f"✓ Current task set to: {task_dir}", Colors.GREEN))
278
+ print(colored(f"✓ Current task set to: {task_json_path.parent.relative_to(repo_root).as_posix()}", Colors.GREEN))
266
279
  print(f"Source: {active.source}")
267
-
268
- if task_json_path.is_file():
269
- _record_start_state(task_json_path, repo_root)
270
-
271
280
  print()
272
281
  print(colored("The hook will now inject context from this task's jsonl files.", Colors.BLUE))
273
282
 
@@ -348,6 +357,11 @@ def cmd_current(args: argparse.Namespace) -> int:
348
357
  payload = {
349
358
  "current_task": task_obj,
350
359
  "source": active.source,
360
+ "session_source": (
361
+ f"session:{active.context_key}"
362
+ if active.context_key
363
+ else None
364
+ ),
351
365
  "stale": active.stale,
352
366
  }
353
367
  # Only present when the read failed, so the healthy shape is unchanged.
@@ -494,7 +508,7 @@ def cmd_list(args: argparse.Namespace) -> int:
494
508
  if filter_status and t.status != filter_status:
495
509
  continue
496
510
  items.append({
497
- "dir": f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}",
511
+ "dir": t.directory.relative_to(repo_root).as_posix(),
498
512
  "id": t.raw.get("id") or dir_name,
499
513
  "title": t.title,
500
514
  "status": t.status,
@@ -533,7 +547,7 @@ def cmd_list(args: argparse.Namespace) -> int:
533
547
  if filter_status and t.status != filter_status:
534
548
  return
535
549
 
536
- relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
550
+ relative_path = t.directory.relative_to(repo_root).as_posix()
537
551
  marker = ""
538
552
  if relative_path == current_task:
539
553
  marker = f" {colored('<- current', Colors.GREEN)}"
@@ -617,7 +631,7 @@ Rename options:
617
631
 
618
632
  List options:
619
633
  --mine, -m Show only tasks assigned to current developer
620
- --status, -s <s> Filter by status (planning, in_progress, review, completed)
634
+ --status, -s <s> Filter by status (planning, in_progress, completed, cancelled)
621
635
  --json Output machine-readable JSON (also available on `current`)
622
636
 
623
637
  Examples:
@@ -769,6 +783,7 @@ def main() -> int:
769
783
  p_phase.add_argument("--route-reason", help="Reason when initialization omits one or more phases")
770
784
  p_phase.add_argument("--route-evidence-ref", action="append", help="Evidence supporting an abbreviated initial route")
771
785
  p_phase.add_argument("--target-phase", help="Earlier phase to revisit, or first affected phase for route change")
786
+ p_phase.add_argument("--return-kind", choices=("contract_change", "execution_rework"), default="contract_change", help="Contract change invalidates authorization; execution rework retains a verified unchanged contract")
772
787
  p_phase.add_argument("--artifact-ref", action="append", help="Task-relative file in the current phase output")
773
788
  p_phase.add_argument("--artifact-revision", help="Current artifact_revision required for acceptance")
774
789
  p_phase.add_argument("--result-ref", help="Task result artifact reference")
@@ -784,6 +799,14 @@ def main() -> int:
784
799
 
785
800
  p_delegate = subparsers.add_parser("delegate-start", help="Start an authorized child task in a Paseo worktree")
786
801
  p_delegate.add_argument("child_task", help="Linked child task directory or name")
802
+ p_delegate_bind = subparsers.add_parser("delegate-bind", help="Freeze a linked child contract within current parent authorization")
803
+ p_delegate_bind.add_argument("child_task")
804
+ p_delegate_bind.add_argument("--result-path", help="Assigned artifact under .trellis/delegated-results/")
805
+ p_delegate_reconcile = subparsers.add_parser("delegate-reconcile", help="Inspect or explicitly retire a verified delegated execution")
806
+ p_delegate_reconcile.add_argument("child_task")
807
+ p_delegate_reconcile.add_argument("--execute", action="store_true")
808
+ p_delegate_reconcile.add_argument("--reason")
809
+ p_delegate_reconcile.add_argument("--approval-ref")
787
810
 
788
811
  add_continuity_commands(subparsers)
789
812
 
@@ -793,6 +816,11 @@ def main() -> int:
793
816
  p_cancel.add_argument("--reason", required=True, help="Why the task is cancelled")
794
817
  p_cancel.add_argument("--evidence-ref", action="append", required=True, help="Task-relative cancellation evidence")
795
818
 
819
+ p_reopen = subparsers.add_parser("reopen", help="Recover a terminal task to planning without implementation authorization")
820
+ p_reopen.add_argument("task_dir", help="Task directory or task name")
821
+ p_reopen.add_argument("--reason", required=True, help="Why the same task must continue")
822
+ p_reopen.add_argument("--evidence-ref", action="append", required=True, help="Recovery decision evidence")
823
+
796
824
  # set-branch
797
825
  p_branch = subparsers.add_parser("set-branch", help="Set git branch")
798
826
  p_branch.add_argument("dir", help="Task directory")
@@ -844,16 +872,18 @@ def main() -> int:
844
872
  p_complete = subparsers.add_parser("complete", help="Complete after Delivery Review and On-Site Closeout Phase acceptance")
845
873
  p_complete.add_argument("task_dir", help="Task directory or task name")
846
874
 
875
+ p_migrate = subparsers.add_parser("migrate-layout", help="Preview or explicitly apply task status directory migration")
876
+ migration_mode = p_migrate.add_mutually_exclusive_group()
877
+ migration_mode.add_argument("--apply", action="store_true", help="Recheck and apply the migration")
878
+ migration_mode.add_argument("--recover", action="store_true", help="Roll back an interrupted relocation")
879
+
847
880
  args = parser.parse_args()
848
881
 
849
882
  if not args.command:
850
883
  show_usage()
851
884
  return 1
852
885
 
853
- if args.command == "continuity":
854
- return continuity_show(args) if args.continuity_action == "show" else continuity_capture(args)
855
- if args.command == "transfer":
856
- return transfer_task_ownership(args)
886
+ from common.task_move import cmd_migrate_layout, layout_lock, require_settled_layout
857
887
 
858
888
  commands = {
859
889
  "create": cmd_create,
@@ -866,8 +896,11 @@ def main() -> int:
866
896
  "workflow": cmd_workflow,
867
897
  "phase": cmd_phase,
868
898
  "delegate-start": cmd_delegate_start,
899
+ "delegate-bind": cmd_delegate_bind,
900
+ "delegate-reconcile": cmd_delegate_reconcile,
869
901
  "complete": cmd_complete_task,
870
902
  "cancel": cmd_cancel_task,
903
+ "reopen": cmd_reopen_task,
871
904
  "set-branch": cmd_set_branch,
872
905
  "set-base-branch": cmd_set_base_branch,
873
906
  "set-scope": cmd_set_scope,
@@ -876,12 +909,26 @@ def main() -> int:
876
909
  "add-subtask": cmd_add_subtask,
877
910
  "remove-subtask": cmd_remove_subtask,
878
911
  "list": cmd_list,
912
+ "migrate-layout": cmd_migrate_layout,
879
913
  }
880
914
 
881
- if args.command in commands:
882
- return commands[args.command](args)
883
- else:
884
- show_usage()
915
+ try:
916
+ with layout_lock(get_repo_root()):
917
+ if args.command != "migrate-layout":
918
+ require_settled_layout(get_repo_root())
919
+ if args.command == "continuity":
920
+ return continuity_show(args) if args.continuity_action == "show" else continuity_capture(args)
921
+ if args.command == "transfer":
922
+ return transfer_task_ownership(args)
923
+ if args.command in commands:
924
+ return commands[args.command](args)
925
+ show_usage()
926
+ return 1
927
+ except TaskWorkflowError as error:
928
+ print(f"{error.code}: {error}", file=sys.stderr)
929
+ return 1
930
+ except (ValueError, OSError) as error:
931
+ print(f"TASK_LAYOUT_WITHHELD: {error}", file=sys.stderr)
885
932
  return 1
886
933
 
887
934
 
@@ -0,0 +1,10 @@
1
+ {
2
+ "schema_version": 2,
3
+ "default_mode": "general",
4
+ "modes": {
5
+ "general": { "default_members": 1, "max_concurrency": 8 },
6
+ "advisor": { "default_members": 3, "max_concurrency": 3 },
7
+ "committee": { "default_members": 3, "max_concurrency": 3 }
8
+ },
9
+ "resources": { "max_live_teammates": 8, "non_batch_reserved": 0 }
10
+ }
@@ -20,7 +20,7 @@ python3 ./.trellis/scripts/get_context.py --mode spec --file <path>
20
20
 
21
21
  ### 任务记录
22
22
 
23
- Trellis task 位于 `.trellis/tasks/`,包含 `task.json`、需求以及任务所需的设计、实施、研究或上下文资产。使用 Trellis CLI 创建和启动任务、初始化和推进阶段路线,以及完成或取消任务。不得手工修改任务状态或阶段状态。
23
+ Trellis task 按 `task.json.status` 存放在 `.trellis/tasks/planning/<task>`、`in_progress/<task>`、`completed/YYYY-MM/<task>` 或 `cancelled/<task>`。创建进入 `planning`,获准实施后启动进入 `in_progress`;目录仅是状态的投影。每个任务包含 `task.json`、需求以及任务所需的设计、实施、研究或上下文资产。使用 Trellis CLI 创建和启动任务、初始化和推进阶段路线,以及完成或取消任务。不得手工修改任务状态或阶段状态。
24
24
 
25
25
  ```bash
26
26
  python3 ./.trellis/scripts/task.py create "<title>" [--slug <name>] [--parent <task>]
@@ -30,11 +30,20 @@ python3 ./.trellis/scripts/task.py validate <task>
30
30
  python3 ./.trellis/scripts/task.py start <task>
31
31
  python3 ./.trellis/scripts/task.py complete <task>
32
32
  python3 ./.trellis/scripts/task.py cancel <task> --reason "<reason>" --evidence-ref <reference>
33
+ python3 ./.trellis/scripts/task.py reopen <task> --reason "<reason>" --evidence-ref <reference>
33
34
  ```
34
35
 
35
36
  使用 `phase artifact`、`accept`、`skip`、`wait`、`block`、`resume`、`return`、`route` 和 `authorize` 执行对应的阶段操作。准确参数以 `task.py --help` 为准。跳过阶段必须提供理由和证据;目标探索与需求核对阶段不可省略。
36
37
 
37
- 只有阶段路线已到达获准的实施入口时,`task.py start` 才会将任务从 `planning` 改为 `in_progress`。分析任务可在交付审查与现场收尾阶段后仍处于 `planning` 时完成。`complete` 和 `cancel` 会清除任务的运行时会话指针;任务记录保留在原位置。
38
+ 只有阶段路线已到达获准的实施入口时,`task.py start` 才会将任务从 `planning` 改为 `in_progress`。分析任务可在交付审查与现场收尾阶段后仍处于 `planning` 时完成。`complete` 将任务移入 `completed/YYYY-MM/<task>`,`cancel` 将任务移入 `cancelled/<task>`,并在同一事务中清除任务的运行时会话指针。
39
+
40
+ 工作流阶段回流与原生任务生命周期恢复分别操作。`completed` 或 `cancelled` 任务需要在原线程继续时,先用 `reopen` 恢复到 `planning`,保留终态事实、取消原因和恢复证据,清除旧运行态、实施授权和依赖准入;工作流阶段保持原记录。随后用 `phase return` 回到最早受影响阶段。实现任务重新接受合同、取得新的实施授权并核验准入后,才由 `start` 进入 `in_progress`。`phase return` 不能直接修改终态任务。
41
+
42
+ `phase return` 默认采用 `contract_change`,使受影响阶段接受和实施授权失效。合同不变的局部实施或证据返工可显式使用 `--return-kind execution_rework`,目标限于实施或交付阶段,可重入当前阶段;CLI 核验需求、已接受合同、授权摘要、路线 revision 和已声明依赖准入仍有效后,保留授权,清理目标阶段及下游的接受事实。不能以此修改目标、合同或绕过待决依赖。普通局部修复可直接在当前活动阶段完成,无需为了记录进展而回流。
43
+
44
+ 新接受的 `implementation_contract`(以及省略设计阶段时新授权的合同)必须包含 `## 验收覆盖` 表,表头固定为 `接受条件 | 工作包 | Owner | 验证入口 | 交付证据 | 停止条件`,至少一行且各单元格非空。CLI 负责结构检查,阶段 Skill 负责逐条核对已接受条件的语义覆盖。既有接受和授权不会因本次升级自动失效;再次接受或授权时执行新检查。
45
+
46
+ 旧布局须先预览 `task.py migrate-layout`,核对后执行 `task.py migrate-layout --apply`;旧 `archive/YYYY-MM` 中已完成的任务保留原月份,其他任务按真实状态放置。迁移会同步 JSON/JSONL 机器引用、会话指针和运行态索引,保留任务身份与父子关系,不重授历史授权。未完成事务会阻止普通任务命令,须用 `task.py migrate-layout --recover` 恢复;输入被外部改动时停止并保留现场。路径或产物内容改变导致接受证据失效时,须经原生阶段接受或 continuity 核对重新收敛。
38
47
 
39
48
  在支持原生实施或检查 agent 的平台上,任务上下文清单(`implement.jsonl`、`check.jsonl`)用于提供相关规范和研究资料。只有这些 agent 会读取清单时才需要维护;清单不能替代任务资产或阶段证据。
40
49
 
@@ -103,7 +103,7 @@ export declare function parseAblationState(value: unknown): AblationStateV1;
103
103
  export declare function stageAblationTransaction(input: StagedAblationInput, options?: StageAblationOptions): LoadedAblationTransaction;
104
104
  export declare function loadAblationTransaction(projectRoot: string, stateRoot?: string): LoadedAblationTransaction | null;
105
105
  export declare function transitionAblationState(transaction: LoadedAblationTransaction, status: AblationStatus): AblationStateV1;
106
- /** Serialize ablate/restore state and project mutations for one project. */
106
+ /** Shared project maintenance boundary; asynchronous operations retain ownership. */
107
107
  export declare function withAblationProjectLock<T>(paths: TransactionPaths, operation: () => T): T;
108
108
  /** Preflight every affected path before restore writes anything. */
109
109
  export declare function collectRestoreConflicts(transaction: LoadedAblationTransaction): AblationConflict[];
@@ -1 +1 @@
1
- {"version":3,"file":"ablation-store.d.ts","sourceRoot":"","sources":["../../src/utils/ablation-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ablation-store.d.ts","sourceRoot":"","sources":["../../src/utils/ablation-store.ts"],"names":[],"mappings":"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"}
@@ -412,8 +412,8 @@ function pidAlive(pid) {
412
412
  process.kill(pid, 0);
413
413
  return true;
414
414
  }
415
- catch {
416
- return false;
415
+ catch (error) {
416
+ return error.code !== "ESRCH";
417
417
  }
418
418
  }
419
419
  function acquireProjectLock(paths) {
@@ -421,41 +421,68 @@ function acquireProjectLock(paths) {
421
421
  for (let attempt = 0; attempt < 2; attempt += 1) {
422
422
  try {
423
423
  const fd = fs.openSync(paths.lockFile, "wx", 0o600);
424
+ const content = JSON.stringify({ pid: process.pid, nonce: randomUUID() });
424
425
  try {
425
- fs.writeSync(fd, String(process.pid));
426
+ fs.writeSync(fd, content);
427
+ const stat = fs.fstatSync(fd);
428
+ return { content, ino: stat.ino, dev: stat.dev };
426
429
  }
427
430
  finally {
428
431
  fs.closeSync(fd);
429
432
  }
430
- return;
431
433
  }
432
434
  catch (error) {
433
435
  if (error.code !== "EEXIST")
434
436
  throw error;
435
437
  }
436
- let holderPid = 0;
437
- try {
438
- holderPid = Number(fs.readFileSync(paths.lockFile, "utf-8").trim());
438
+ const stat = fs.lstatSync(paths.lockFile);
439
+ if (!stat.isFile() || stat.isSymbolicLink()) {
440
+ throw new Error("Unknown Trellis maintenance lock; inspect it before retrying.");
439
441
  }
440
- catch {
441
- continue;
442
+ const content = fs.readFileSync(paths.lockFile, "utf-8").trim();
443
+ let holderPid;
444
+ if (/^[1-9]\d*$/.test(content))
445
+ holderPid = Number(content);
446
+ else {
447
+ try {
448
+ const record = JSON.parse(content);
449
+ if (record &&
450
+ typeof record === "object" &&
451
+ "pid" in record &&
452
+ "nonce" in record &&
453
+ typeof record.nonce === "string")
454
+ holderPid = record.pid;
455
+ }
456
+ catch {
457
+ // An incomplete or unknown lock is not evidence that its owner exited.
458
+ }
442
459
  }
443
- if (holderPid && pidAlive(holderPid)) {
444
- throw new Error(`Another Trellis ablation operation is already in progress for this project (pid ${holderPid}).`);
460
+ if (typeof holderPid !== "number" ||
461
+ !Number.isSafeInteger(holderPid) ||
462
+ holderPid <= 0) {
463
+ throw new Error("Unknown Trellis maintenance lock; inspect it before retrying.");
445
464
  }
446
- try {
447
- fs.unlinkSync(paths.lockFile);
465
+ if (pidAlive(holderPid)) {
466
+ throw new Error(`Another Trellis maintenance operation is already in progress for this project (pid ${holderPid}).`);
448
467
  }
449
- catch {
450
- // A concurrent process changed the lock; retry the atomic create once.
468
+ const current = fs.lstatSync(paths.lockFile);
469
+ if (current.ino !== stat.ino ||
470
+ current.dev !== stat.dev ||
471
+ fs.readFileSync(paths.lockFile, "utf-8").trim() !== content) {
472
+ throw new Error("Trellis maintenance lock changed during inspection.");
451
473
  }
474
+ fs.unlinkSync(paths.lockFile);
452
475
  }
453
476
  throw new Error("Unable to acquire the Trellis ablation-operation lock; retry the command.");
454
477
  }
455
- function releaseProjectLock(paths) {
478
+ function releaseProjectLock(paths, lock) {
456
479
  try {
457
- const holder = fs.readFileSync(paths.lockFile, "utf-8").trim();
458
- if (holder === String(process.pid)) {
480
+ const stat = fs.lstatSync(paths.lockFile);
481
+ if (stat.isFile() &&
482
+ !stat.isSymbolicLink() &&
483
+ stat.ino === lock.ino &&
484
+ stat.dev === lock.dev &&
485
+ fs.readFileSync(paths.lockFile, "utf-8") === lock.content) {
459
486
  fs.unlinkSync(paths.lockFile);
460
487
  }
461
488
  }
@@ -463,14 +490,21 @@ function releaseProjectLock(paths) {
463
490
  // Already released or replaced by another process.
464
491
  }
465
492
  }
466
- /** Serialize ablate/restore state and project mutations for one project. */
493
+ /** Shared project maintenance boundary; asynchronous operations retain ownership. */
467
494
  export function withAblationProjectLock(paths, operation) {
468
- acquireProjectLock(paths);
495
+ const lock = acquireProjectLock(paths);
496
+ let pending = false;
469
497
  try {
470
- return operation();
498
+ const result = operation();
499
+ if (result instanceof Promise) {
500
+ pending = true;
501
+ return result.finally(() => releaseProjectLock(paths, lock));
502
+ }
503
+ return result;
471
504
  }
472
505
  finally {
473
- releaseProjectLock(paths);
506
+ if (!pending)
507
+ releaseProjectLock(paths, lock);
474
508
  }
475
509
  }
476
510
  /** Preflight every affected path before restore writes anything. */
@@ -693,7 +727,7 @@ export function verifyAblatedState(transaction) {
693
727
  export function restoreAblationTransaction(transaction, options = {}) {
694
728
  const projectRoot = canonicalProjectRoot(transaction.state.projectRoot);
695
729
  const paths = getTransactionPaths(projectRoot, transaction.paths.stateRoot);
696
- acquireProjectLock(paths);
730
+ const lock = acquireProjectLock(paths);
697
731
  try {
698
732
  const reloaded = loadAblationTransaction(projectRoot, paths.stateRoot);
699
733
  if (!reloaded) {
@@ -717,7 +751,7 @@ export function restoreAblationTransaction(transaction, options = {}) {
717
751
  deleteAblationTransaction(transaction);
718
752
  }
719
753
  finally {
720
- releaseProjectLock(paths);
754
+ releaseProjectLock(paths, lock);
721
755
  }
722
756
  }
723
757
  /** Roll back this process's partial ablation while holding the project lock. */