@pennixrv/trellis 0.7.1 → 0.7.2-beta.2

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 (321) 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 +42 -14
  43. package/dist/commands/teamwork/artifact.d.ts.map +1 -1
  44. package/dist/commands/teamwork/artifact.js +188 -85
  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 +23 -0
  51. package/dist/commands/teamwork/batch.d.ts.map +1 -0
  52. package/dist/commands/teamwork/batch.js +415 -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 +33 -6
  63. package/dist/commands/teamwork/coordinator.d.ts.map +1 -1
  64. package/dist/commands/teamwork/coordinator.js +948 -342
  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 +25 -7
  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 +163 -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 +55 -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 +401 -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/migrations/manifests/0.7.2-beta.1.json +11 -0
  203. package/dist/migrations/manifests/0.7.2-beta.2.json +11 -0
  204. package/dist/templates/claude/hooks/statusline.py +9 -4
  205. package/dist/templates/codex/hooks/session-start.py +14 -11
  206. package/dist/templates/codex/hooks.json +10 -0
  207. package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/workflow.md +2 -0
  208. package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/workflow.md +2 -0
  209. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +1 -1
  210. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +1 -1
  211. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +5 -5
  212. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +3 -2
  213. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +1 -1
  214. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +1 -1
  215. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-teamwork.md +14 -2
  216. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -2
  217. package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +2 -1
  218. package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +2 -2
  219. package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/workflow.md +8 -0
  220. package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/mcp-setup.md +4 -2
  221. package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-task-planning.md +2 -2
  222. package/dist/templates/common/bundled-skills/trellis-task-implementation/workflow.md +6 -0
  223. package/dist/templates/common/bundled-skills/trellis-teamwork/SKILL.md +18 -3
  224. package/dist/templates/common/bundled-skills/trellis-teamwork/references/coordination-modes.md +34 -3
  225. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-configuration-matrix.md +37 -26
  226. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +128 -14
  227. package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +152 -20
  228. package/dist/templates/common/commands/start.md +2 -2
  229. package/dist/templates/common/skills/before-dev.md +1 -4
  230. package/dist/templates/common/skills/break-loop.md +7 -18
  231. package/dist/templates/common/skills/check.md +7 -11
  232. package/dist/templates/common/skills/{brainstorm.md → request-intake.md} +5 -3
  233. package/dist/templates/common/skills/update-spec.md +5 -7
  234. package/dist/templates/copilot/hooks/session-start.py +14 -11
  235. package/dist/templates/copilot/index.d.ts +1 -6
  236. package/dist/templates/copilot/index.d.ts.map +1 -1
  237. package/dist/templates/copilot/index.js +1 -14
  238. package/dist/templates/copilot/index.js.map +1 -1
  239. package/dist/templates/extract.js +4 -0
  240. package/dist/templates/extract.js.map +1 -1
  241. package/dist/templates/markdown/spec/backend/index.md.txt +4 -35
  242. package/dist/templates/markdown/spec/frontend/index.md.txt +4 -36
  243. package/dist/templates/omp/extensions/trellis/index.ts.txt +2 -2
  244. package/dist/templates/opencode/lib/session-utils.js +301 -209
  245. package/dist/templates/pi/extensions/trellis/index.ts.txt +17 -1
  246. package/dist/templates/shared-hooks/guard-teamwork-wait.py +58 -0
  247. package/dist/templates/shared-hooks/index.d.ts +1 -1
  248. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  249. package/dist/templates/shared-hooks/index.js +1 -0
  250. package/dist/templates/shared-hooks/index.js.map +1 -1
  251. package/dist/templates/shared-hooks/inject-workflow-state.py +8 -3
  252. package/dist/templates/shared-hooks/session-start.py +16 -12
  253. package/dist/templates/snow/SNOW.md +1 -1
  254. package/dist/templates/trellis/agents/delegated-worktree-primary-agent.md +9 -0
  255. package/dist/templates/trellis/agents/profiles/architecture.md +9 -0
  256. package/dist/templates/trellis/agents/profiles/diagnosis.md +9 -0
  257. package/dist/templates/trellis/agents/profiles/exploration.md +9 -0
  258. package/dist/templates/trellis/agents/profiles/research.md +9 -0
  259. package/dist/templates/trellis/agents/profiles/review.md +9 -0
  260. package/dist/templates/trellis/agents/profiles/verification.md +9 -0
  261. package/dist/templates/trellis/agents/teammate-profiles.json +56 -4
  262. package/dist/templates/trellis/agents/teammate.md +4 -52
  263. package/dist/templates/trellis/config.yaml +2 -5
  264. package/dist/templates/trellis/index.d.ts +4 -0
  265. package/dist/templates/trellis/index.d.ts.map +1 -1
  266. package/dist/templates/trellis/index.js +22 -4
  267. package/dist/templates/trellis/index.js.map +1 -1
  268. package/dist/templates/trellis/scripts/add_session.py +1 -1
  269. package/dist/templates/trellis/scripts/common/active_task.py +3 -47
  270. package/dist/templates/trellis/scripts/common/config.py +5 -0
  271. package/dist/templates/trellis/scripts/common/paths.py +9 -25
  272. package/dist/templates/trellis/scripts/common/safe_commit.py +8 -8
  273. package/dist/templates/trellis/scripts/common/task_context.py +11 -51
  274. package/dist/templates/trellis/scripts/common/task_continuity.py +191 -60
  275. package/dist/templates/trellis/scripts/common/task_layout.py +80 -0
  276. package/dist/templates/trellis/scripts/common/task_move.py +345 -0
  277. package/dist/templates/trellis/scripts/common/task_store.py +51 -57
  278. package/dist/templates/trellis/scripts/common/task_utils.py +23 -19
  279. package/dist/templates/trellis/scripts/common/task_workflow.py +395 -49
  280. package/dist/templates/trellis/scripts/common/tasks.py +6 -5
  281. package/dist/templates/trellis/scripts/hooks/linear_sync.py +13 -3
  282. package/dist/templates/trellis/scripts/task.py +76 -24
  283. package/dist/templates/trellis/teamwork.json +10 -0
  284. package/dist/templates/trellis/workflow.md +11 -2
  285. package/dist/utils/ablation-store.d.ts +1 -1
  286. package/dist/utils/ablation-store.d.ts.map +1 -1
  287. package/dist/utils/ablation-store.js +58 -24
  288. package/dist/utils/ablation-store.js.map +1 -1
  289. package/dist/utils/asset-backup.d.ts +138 -0
  290. package/dist/utils/asset-backup.d.ts.map +1 -0
  291. package/dist/utils/asset-backup.js +525 -0
  292. package/dist/utils/asset-backup.js.map +1 -0
  293. package/dist/utils/atomic-write.d.ts +1 -1
  294. package/dist/utils/atomic-write.d.ts.map +1 -1
  295. package/dist/utils/atomic-write.js +6 -3
  296. package/dist/utils/atomic-write.js.map +1 -1
  297. package/dist/utils/manifest-prune.d.ts +4 -1
  298. package/dist/utils/manifest-prune.d.ts.map +1 -1
  299. package/dist/utils/manifest-prune.js +17 -6
  300. package/dist/utils/manifest-prune.js.map +1 -1
  301. package/dist/utils/template-fetcher.d.ts +4 -19
  302. package/dist/utils/template-fetcher.d.ts.map +1 -1
  303. package/dist/utils/template-fetcher.js +9 -45
  304. package/dist/utils/template-fetcher.js.map +1 -1
  305. package/dist/utils/template-hash.d.ts +5 -4
  306. package/dist/utils/template-hash.d.ts.map +1 -1
  307. package/dist/utils/template-hash.js +24 -17
  308. package/dist/utils/template-hash.js.map +1 -1
  309. package/package.json +3 -2
  310. package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
  311. package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -13
  312. package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
  313. package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -170
  314. package/dist/templates/copilot/prompts/check.prompt.md +0 -110
  315. package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
  316. package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -22
  317. package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
  318. package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
  319. package/dist/templates/copilot/prompts/record-session.prompt.md +0 -58
  320. package/dist/templates/copilot/prompts/start.prompt.md +0 -65
  321. 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."""
@@ -237,6 +247,11 @@ def cmd_start(args: argparse.Namespace) -> int:
237
247
  return 1
238
248
 
239
249
  task_json_path = full_path / FILE_TASK_JSON
250
+ if task_json_path.is_file():
251
+ task_data, _ = read_json_checked(task_json_path)
252
+ if task_data is not None and task_data.get("status") in {"completed", "cancelled"}:
253
+ print(colored(f"TASK_PHASE_TASK_TERMINAL: task is already {task_data['status']}", Colors.RED), file=sys.stderr)
254
+ return 1
240
255
 
241
256
  if not resolve_context_key():
242
257
  # Degraded mode: no session identity available.
@@ -256,18 +271,17 @@ def cmd_start(args: argparse.Namespace) -> int:
256
271
 
257
272
  # Enter implementation only when the accepted phase contract authorizes it.
258
273
  if task_json_path.is_file():
259
- _record_start_state(task_json_path, repo_root, " (degraded)")
274
+ task_json_path = _record_start_state(task_json_path, repo_root, " (degraded)")
260
275
  run_task_hooks("after_start", task_json_path, repo_root)
261
276
  return 0
262
277
 
278
+ if task_json_path.is_file():
279
+ task_json_path = _record_start_state(task_json_path, repo_root)
280
+ task_dir = task_json_path.parent.relative_to(repo_root).as_posix()
263
281
  active = set_active_task(task_dir, repo_root)
264
282
  if active:
265
- print(colored(f"✓ Current task set to: {task_dir}", Colors.GREEN))
283
+ print(colored(f"✓ Current task set to: {task_json_path.parent.relative_to(repo_root).as_posix()}", Colors.GREEN))
266
284
  print(f"Source: {active.source}")
267
-
268
- if task_json_path.is_file():
269
- _record_start_state(task_json_path, repo_root)
270
-
271
285
  print()
272
286
  print(colored("The hook will now inject context from this task's jsonl files.", Colors.BLUE))
273
287
 
@@ -348,6 +362,11 @@ def cmd_current(args: argparse.Namespace) -> int:
348
362
  payload = {
349
363
  "current_task": task_obj,
350
364
  "source": active.source,
365
+ "session_source": (
366
+ f"session:{active.context_key}"
367
+ if active.context_key
368
+ else None
369
+ ),
351
370
  "stale": active.stale,
352
371
  }
353
372
  # Only present when the read failed, so the healthy shape is unchanged.
@@ -494,7 +513,7 @@ def cmd_list(args: argparse.Namespace) -> int:
494
513
  if filter_status and t.status != filter_status:
495
514
  continue
496
515
  items.append({
497
- "dir": f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}",
516
+ "dir": t.directory.relative_to(repo_root).as_posix(),
498
517
  "id": t.raw.get("id") or dir_name,
499
518
  "title": t.title,
500
519
  "status": t.status,
@@ -533,7 +552,7 @@ def cmd_list(args: argparse.Namespace) -> int:
533
552
  if filter_status and t.status != filter_status:
534
553
  return
535
554
 
536
- relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
555
+ relative_path = t.directory.relative_to(repo_root).as_posix()
537
556
  marker = ""
538
557
  if relative_path == current_task:
539
558
  marker = f" {colored('<- current', Colors.GREEN)}"
@@ -617,7 +636,7 @@ Rename options:
617
636
 
618
637
  List options:
619
638
  --mine, -m Show only tasks assigned to current developer
620
- --status, -s <s> Filter by status (planning, in_progress, review, completed)
639
+ --status, -s <s> Filter by status (planning, in_progress, completed, cancelled)
621
640
  --json Output machine-readable JSON (also available on `current`)
622
641
 
623
642
  Examples:
@@ -769,6 +788,7 @@ def main() -> int:
769
788
  p_phase.add_argument("--route-reason", help="Reason when initialization omits one or more phases")
770
789
  p_phase.add_argument("--route-evidence-ref", action="append", help="Evidence supporting an abbreviated initial route")
771
790
  p_phase.add_argument("--target-phase", help="Earlier phase to revisit, or first affected phase for route change")
791
+ p_phase.add_argument("--return-kind", choices=("contract_change", "execution_rework", "withdrawal"), default="contract_change", help="Classify a return or explicitly record user withdrawal")
772
792
  p_phase.add_argument("--artifact-ref", action="append", help="Task-relative file in the current phase output")
773
793
  p_phase.add_argument("--artifact-revision", help="Current artifact_revision required for acceptance")
774
794
  p_phase.add_argument("--result-ref", help="Task result artifact reference")
@@ -784,6 +804,14 @@ def main() -> int:
784
804
 
785
805
  p_delegate = subparsers.add_parser("delegate-start", help="Start an authorized child task in a Paseo worktree")
786
806
  p_delegate.add_argument("child_task", help="Linked child task directory or name")
807
+ p_delegate_bind = subparsers.add_parser("delegate-bind", help="Freeze a linked child contract within current parent authorization")
808
+ p_delegate_bind.add_argument("child_task")
809
+ p_delegate_bind.add_argument("--result-path", help="Assigned artifact under .trellis/delegated-results/")
810
+ p_delegate_reconcile = subparsers.add_parser("delegate-reconcile", help="Inspect or explicitly retire a verified delegated execution")
811
+ p_delegate_reconcile.add_argument("child_task")
812
+ p_delegate_reconcile.add_argument("--execute", action="store_true")
813
+ p_delegate_reconcile.add_argument("--reason")
814
+ p_delegate_reconcile.add_argument("--approval-ref")
787
815
 
788
816
  add_continuity_commands(subparsers)
789
817
 
@@ -793,6 +821,11 @@ def main() -> int:
793
821
  p_cancel.add_argument("--reason", required=True, help="Why the task is cancelled")
794
822
  p_cancel.add_argument("--evidence-ref", action="append", required=True, help="Task-relative cancellation evidence")
795
823
 
824
+ p_reopen = subparsers.add_parser("reopen", help="Recover a terminal task to planning without implementation authorization")
825
+ p_reopen.add_argument("task_dir", help="Task directory or task name")
826
+ p_reopen.add_argument("--reason", required=True, help="Why the same task must continue")
827
+ p_reopen.add_argument("--evidence-ref", action="append", required=True, help="Recovery decision evidence")
828
+
796
829
  # set-branch
797
830
  p_branch = subparsers.add_parser("set-branch", help="Set git branch")
798
831
  p_branch.add_argument("dir", help="Task directory")
@@ -844,16 +877,18 @@ def main() -> int:
844
877
  p_complete = subparsers.add_parser("complete", help="Complete after Delivery Review and On-Site Closeout Phase acceptance")
845
878
  p_complete.add_argument("task_dir", help="Task directory or task name")
846
879
 
880
+ p_migrate = subparsers.add_parser("migrate-layout", help="Preview or explicitly apply task status directory migration")
881
+ migration_mode = p_migrate.add_mutually_exclusive_group()
882
+ migration_mode.add_argument("--apply", action="store_true", help="Recheck and apply the migration")
883
+ migration_mode.add_argument("--recover", action="store_true", help="Roll back an interrupted relocation")
884
+
847
885
  args = parser.parse_args()
848
886
 
849
887
  if not args.command:
850
888
  show_usage()
851
889
  return 1
852
890
 
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)
891
+ from common.task_move import cmd_migrate_layout, layout_lock, require_settled_layout
857
892
 
858
893
  commands = {
859
894
  "create": cmd_create,
@@ -866,8 +901,11 @@ def main() -> int:
866
901
  "workflow": cmd_workflow,
867
902
  "phase": cmd_phase,
868
903
  "delegate-start": cmd_delegate_start,
904
+ "delegate-bind": cmd_delegate_bind,
905
+ "delegate-reconcile": cmd_delegate_reconcile,
869
906
  "complete": cmd_complete_task,
870
907
  "cancel": cmd_cancel_task,
908
+ "reopen": cmd_reopen_task,
871
909
  "set-branch": cmd_set_branch,
872
910
  "set-base-branch": cmd_set_base_branch,
873
911
  "set-scope": cmd_set_scope,
@@ -876,12 +914,26 @@ def main() -> int:
876
914
  "add-subtask": cmd_add_subtask,
877
915
  "remove-subtask": cmd_remove_subtask,
878
916
  "list": cmd_list,
917
+ "migrate-layout": cmd_migrate_layout,
879
918
  }
880
919
 
881
- if args.command in commands:
882
- return commands[args.command](args)
883
- else:
884
- show_usage()
920
+ try:
921
+ with layout_lock(get_repo_root()):
922
+ if args.command != "migrate-layout":
923
+ require_settled_layout(get_repo_root())
924
+ if args.command == "continuity":
925
+ return continuity_show(args) if args.continuity_action == "show" else continuity_capture(args)
926
+ if args.command == "transfer":
927
+ return transfer_task_ownership(args)
928
+ if args.command in commands:
929
+ return commands[args.command](args)
930
+ show_usage()
931
+ return 1
932
+ except TaskWorkflowError as error:
933
+ print(f"{error.code}: {error}", file=sys.stderr)
934
+ return 1
935
+ except (ValueError, OSError) as error:
936
+ print(f"TASK_LAYOUT_WITHHELD: {error}", file=sys.stderr)
885
937
  return 1
886
938
 
887
939
 
@@ -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. */