@rig-c/create 1.0.2 → 1.1.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 (629) hide show
  1. package/README.md +21 -5
  2. package/docs/AGENT-REFERENCE.md +62 -0
  3. package/docs/CHECKS.md +45 -9
  4. package/docs/INTERVIEW.md +19 -7
  5. package/docs/PROJECT.md +10 -0
  6. package/package.json +4 -1
  7. package/scripts/npm-json.ts +74 -0
  8. package/scripts/verify-example-seals.ts +148 -0
  9. package/src/agent-guide.ts +7 -4
  10. package/src/check.ts +7 -4
  11. package/src/choice-writers.ts +3 -14
  12. package/src/cli.ts +1 -0
  13. package/src/feedback.ts +3 -10
  14. package/src/fix-ledger.ts +1 -1
  15. package/src/job-acts.ts +4 -0
  16. package/src/job-host-state.ts +154 -0
  17. package/src/job-host.ts +37 -14
  18. package/src/job-outbox.ts +5 -3
  19. package/src/job-publication.ts +17 -13
  20. package/src/job-runtime.ts +17 -4
  21. package/src/next.ts +110 -24
  22. package/src/parts-review.ts +4 -11
  23. package/src/reopen.ts +72 -9
  24. package/src/review-act.ts +49 -14
  25. package/src/review-package.ts +3 -1
  26. package/src/rig-version.ts +219 -8
  27. package/src/scene-writers.ts +5 -3
  28. package/src/scene.ts +14 -4
  29. package/src/signable.ts +25 -3
  30. package/src/signoff-checkpoint-act.ts +2 -16
  31. package/src/signoff.ts +3 -15
  32. package/src/waypoints.ts +20 -9
  33. package/templates/agent-guide.md +2 -24
  34. package/templates/collection.json +10 -2
  35. package/templates/example-chain/.gitignore +6 -0
  36. package/templates/example-chain/.npmignore +1 -0
  37. package/templates/example-chain/AGENT-REFERENCE.md +62 -0
  38. package/templates/example-chain/AGENTS.md +4 -26
  39. package/templates/example-chain/README.md +1 -1
  40. package/templates/example-chain/chain.json +385 -319
  41. package/templates/example-chain/checkpoints/0a-interview-options/.gitignore +6 -0
  42. package/templates/example-chain/checkpoints/0a-interview-options/.npmignore +1 -0
  43. package/templates/example-chain/checkpoints/0a-interview-options/AGENT-REFERENCE.md +62 -0
  44. package/templates/example-chain/checkpoints/0a-interview-options/AGENTS.md +4 -26
  45. package/templates/example-chain/checkpoints/0a-interview-options/project.json +22 -5
  46. package/templates/example-chain/checkpoints/0a-interview-options.json +7 -3
  47. package/templates/example-chain/checkpoints/0b-plan-pending/.gitignore +6 -0
  48. package/templates/example-chain/checkpoints/0b-plan-pending/.npmignore +1 -0
  49. package/templates/example-chain/checkpoints/0b-plan-pending/AGENT-REFERENCE.md +62 -0
  50. package/templates/example-chain/checkpoints/0b-plan-pending/AGENTS.md +4 -26
  51. package/templates/example-chain/checkpoints/0b-plan-pending/project.json +22 -5
  52. package/templates/example-chain/checkpoints/0b-plan-pending.json +7 -3
  53. package/templates/example-chain/checkpoints/1-parts-verdict/.gitignore +6 -0
  54. package/templates/example-chain/checkpoints/1-parts-verdict/.npmignore +1 -0
  55. package/templates/example-chain/checkpoints/1-parts-verdict/AGENT-REFERENCE.md +62 -0
  56. package/templates/example-chain/checkpoints/1-parts-verdict/AGENTS.md +4 -26
  57. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/config.json +88 -0
  58. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/painting_in_rig.png +0 -0
  59. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts/background.png +0 -0
  60. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts/eye_l.png +0 -0
  61. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts/eye_r.png +0 -0
  62. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts/face.png +0 -0
  63. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts/lantern.png +0 -0
  64. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts/torso.png +0 -0
  65. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/parts.json +188 -0
  66. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/input/recomposite_rig.png +0 -0
  67. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/interview-requirements.json +8 -0
  68. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/check/build/run.json +40 -0
  69. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/check/build/skeleton.atlas +28 -0
  70. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/check/build/skeleton.json +674 -0
  71. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/check/build/skeleton.model.json +1072 -0
  72. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/check/build/skeleton.png +0 -0
  73. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/images/background.png +0 -0
  74. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/images/eye_l.png +0 -0
  75. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/images/eye_r.png +0 -0
  76. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/images/face.png +0 -0
  77. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/images/lantern.png +0 -0
  78. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/images/torso.png +0 -0
  79. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/mesh_report.json +15 -0
  80. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/motion.json +137 -0
  81. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/rig.json +536 -0
  82. package/templates/example-chain/checkpoints/1-parts-verdict/derived/drafts/chain/out/run.json +52 -0
  83. package/templates/example-chain/checkpoints/1-parts-verdict/project.json +24 -7
  84. package/templates/example-chain/checkpoints/1-parts-verdict.json +9 -5
  85. package/templates/example-chain/checkpoints/2-rigging-answers/.gitignore +6 -0
  86. package/templates/example-chain/checkpoints/2-rigging-answers/.npmignore +1 -0
  87. package/templates/example-chain/checkpoints/2-rigging-answers/AGENT-REFERENCE.md +62 -0
  88. package/templates/example-chain/checkpoints/2-rigging-answers/AGENTS.md +4 -26
  89. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/config.json +88 -0
  90. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/painting_in_rig.png +0 -0
  91. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts/background.png +0 -0
  92. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts/eye_l.png +0 -0
  93. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts/eye_r.png +0 -0
  94. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts/face.png +0 -0
  95. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts/lantern.png +0 -0
  96. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts/torso.png +0 -0
  97. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/parts.json +188 -0
  98. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/input/recomposite_rig.png +0 -0
  99. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/interview-requirements.json +8 -0
  100. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/check/build/run.json +40 -0
  101. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/check/build/skeleton.atlas +28 -0
  102. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/check/build/skeleton.json +674 -0
  103. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/check/build/skeleton.model.json +1072 -0
  104. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/check/build/skeleton.png +0 -0
  105. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/images/background.png +0 -0
  106. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/images/eye_l.png +0 -0
  107. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/images/eye_r.png +0 -0
  108. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/images/face.png +0 -0
  109. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/images/lantern.png +0 -0
  110. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/images/torso.png +0 -0
  111. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/mesh_report.json +15 -0
  112. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/motion.json +137 -0
  113. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/rig.json +536 -0
  114. package/templates/example-chain/checkpoints/2-rigging-answers/derived/drafts/chain/out/run.json +52 -0
  115. package/templates/example-chain/checkpoints/2-rigging-answers/project.json +24 -7
  116. package/templates/example-chain/checkpoints/2-rigging-answers.json +21 -5
  117. package/templates/example-chain/checkpoints/3-rig-review/.gitignore +6 -0
  118. package/templates/example-chain/checkpoints/3-rig-review/.npmignore +1 -0
  119. package/templates/example-chain/checkpoints/3-rig-review/AGENT-REFERENCE.md +62 -0
  120. package/templates/example-chain/checkpoints/3-rig-review/AGENTS.md +4 -26
  121. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/config.json +88 -0
  122. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/painting_in_rig.png +0 -0
  123. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts/background.png +0 -0
  124. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts/eye_l.png +0 -0
  125. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts/eye_r.png +0 -0
  126. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts/face.png +0 -0
  127. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts/lantern.png +0 -0
  128. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts/torso.png +0 -0
  129. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/parts.json +188 -0
  130. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/input/recomposite_rig.png +0 -0
  131. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/interview-requirements.json +8 -0
  132. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/check/build/run.json +40 -0
  133. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/check/build/skeleton.atlas +28 -0
  134. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/check/build/skeleton.json +674 -0
  135. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/check/build/skeleton.model.json +1072 -0
  136. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/check/build/skeleton.png +0 -0
  137. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/images/background.png +0 -0
  138. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/images/eye_l.png +0 -0
  139. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/images/eye_r.png +0 -0
  140. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/images/face.png +0 -0
  141. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/images/lantern.png +0 -0
  142. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/images/torso.png +0 -0
  143. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/mesh_report.json +15 -0
  144. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/motion.json +137 -0
  145. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/rig.json +536 -0
  146. package/templates/example-chain/checkpoints/3-rig-review/derived/drafts/chain/out/run.json +52 -0
  147. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/config.json +88 -0
  148. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/interview-requirements.json +8 -0
  149. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/check/build/run.json +40 -0
  150. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/check/build/skeleton.atlas +28 -0
  151. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/check/build/skeleton.json +674 -0
  152. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/check/build/skeleton.model.json +1072 -0
  153. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/check/build/skeleton.png +0 -0
  154. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts/background.png +0 -0
  155. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts/eye_l.png +0 -0
  156. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts/eye_r.png +0 -0
  157. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts/face.png +0 -0
  158. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts/lantern.png +0 -0
  159. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts/torso.png +0 -0
  160. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/parts.json +188 -0
  161. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/images/background.png +0 -0
  162. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/images/eye_l.png +0 -0
  163. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/images/eye_r.png +0 -0
  164. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/images/face.png +0 -0
  165. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/images/lantern.png +0 -0
  166. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/images/torso.png +0 -0
  167. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/mesh_report.json +15 -0
  168. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/motion.json +137 -0
  169. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/rig.json +536 -0
  170. package/templates/example-chain/checkpoints/3-rig-review/derived/review/v1/out/rig/run.json +52 -0
  171. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/motion.json +1 -1
  172. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/turn.json +108 -0
  173. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/check/build/run.json +40 -0
  174. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/check/build/skeleton.atlas +28 -0
  175. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/check/build/skeleton.json +674 -0
  176. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/check/build/skeleton.model.json +1072 -0
  177. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/check/build/skeleton.png +0 -0
  178. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/painting_in_rig.png +0 -0
  179. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts/background.png +0 -0
  180. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts/eye_l.png +0 -0
  181. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts/eye_r.png +0 -0
  182. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts/face.png +0 -0
  183. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts/lantern.png +0 -0
  184. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts/torso.png +0 -0
  185. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/parts.json +188 -0
  186. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/images/background.png +0 -0
  187. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/images/eye_l.png +0 -0
  188. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/images/eye_r.png +0 -0
  189. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/images/face.png +0 -0
  190. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/images/lantern.png +0 -0
  191. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/images/torso.png +0 -0
  192. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/mesh_report.json +15 -0
  193. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/motion.json +137 -0
  194. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/rig.json +536 -0
  195. package/templates/example-chain/checkpoints/3-rig-review/outputs/v1/out/rig/run.json +52 -0
  196. package/templates/example-chain/checkpoints/3-rig-review/project.json +77 -28
  197. package/templates/example-chain/checkpoints/3-rig-review.json +19 -11
  198. package/templates/example-chain/checkpoints/4-direction-post/.gitignore +6 -0
  199. package/templates/example-chain/checkpoints/4-direction-post/.npmignore +1 -0
  200. package/templates/example-chain/checkpoints/4-direction-post/AGENT-REFERENCE.md +62 -0
  201. package/templates/example-chain/checkpoints/4-direction-post/AGENTS.md +4 -26
  202. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/config.json +88 -0
  203. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/painting_in_rig.png +0 -0
  204. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts/background.png +0 -0
  205. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts/eye_l.png +0 -0
  206. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts/eye_r.png +0 -0
  207. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts/face.png +0 -0
  208. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts/lantern.png +0 -0
  209. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts/torso.png +0 -0
  210. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/parts.json +188 -0
  211. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/input/recomposite_rig.png +0 -0
  212. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/interview-requirements.json +8 -0
  213. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/check/build/run.json +40 -0
  214. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/check/build/skeleton.atlas +28 -0
  215. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/check/build/skeleton.json +674 -0
  216. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/check/build/skeleton.model.json +1072 -0
  217. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/check/build/skeleton.png +0 -0
  218. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/images/background.png +0 -0
  219. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/images/eye_l.png +0 -0
  220. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/images/eye_r.png +0 -0
  221. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/images/face.png +0 -0
  222. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/images/lantern.png +0 -0
  223. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/images/torso.png +0 -0
  224. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/mesh_report.json +15 -0
  225. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/motion.json +137 -0
  226. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/rig.json +536 -0
  227. package/templates/example-chain/checkpoints/4-direction-post/derived/drafts/chain/out/run.json +52 -0
  228. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/config.json +88 -0
  229. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/interview-requirements.json +8 -0
  230. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/check/build/run.json +40 -0
  231. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/check/build/skeleton.atlas +28 -0
  232. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/check/build/skeleton.json +674 -0
  233. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/check/build/skeleton.model.json +1072 -0
  234. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/check/build/skeleton.png +0 -0
  235. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts/background.png +0 -0
  236. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts/eye_l.png +0 -0
  237. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts/eye_r.png +0 -0
  238. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts/face.png +0 -0
  239. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts/lantern.png +0 -0
  240. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts/torso.png +0 -0
  241. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/parts.json +188 -0
  242. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/images/background.png +0 -0
  243. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/images/eye_l.png +0 -0
  244. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/images/eye_r.png +0 -0
  245. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/images/face.png +0 -0
  246. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/images/lantern.png +0 -0
  247. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/images/torso.png +0 -0
  248. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/mesh_report.json +15 -0
  249. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/motion.json +137 -0
  250. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/rig.json +536 -0
  251. package/templates/example-chain/checkpoints/4-direction-post/derived/review/v1/out/rig/run.json +52 -0
  252. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/camera_depth.json +2 -2
  253. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/motion.json +1 -1
  254. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/post_composition.json +2 -2
  255. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/post_timing.json +2 -2
  256. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/turn.json +108 -0
  257. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/check/build/run.json +40 -0
  258. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/check/build/skeleton.atlas +28 -0
  259. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/check/build/skeleton.json +674 -0
  260. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/check/build/skeleton.model.json +1072 -0
  261. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/check/build/skeleton.png +0 -0
  262. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/painting_in_rig.png +0 -0
  263. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts/background.png +0 -0
  264. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts/eye_l.png +0 -0
  265. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts/eye_r.png +0 -0
  266. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts/face.png +0 -0
  267. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts/lantern.png +0 -0
  268. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts/torso.png +0 -0
  269. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/parts.json +188 -0
  270. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/images/background.png +0 -0
  271. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/images/eye_l.png +0 -0
  272. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/images/eye_r.png +0 -0
  273. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/images/face.png +0 -0
  274. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/images/lantern.png +0 -0
  275. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/images/torso.png +0 -0
  276. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/mesh_report.json +15 -0
  277. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/motion.json +137 -0
  278. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/rig.json +536 -0
  279. package/templates/example-chain/checkpoints/4-direction-post/outputs/v1/out/rig/run.json +52 -0
  280. package/templates/example-chain/checkpoints/4-direction-post/project.json +106 -57
  281. package/templates/example-chain/checkpoints/4-direction-post.json +22 -14
  282. package/templates/example-chain/derived/drafts/chain/config.json +88 -0
  283. package/templates/example-chain/derived/drafts/chain/input/painting_in_rig.png +0 -0
  284. package/templates/example-chain/derived/drafts/chain/input/parts/background.png +0 -0
  285. package/templates/example-chain/derived/drafts/chain/input/parts/eye_l.png +0 -0
  286. package/templates/example-chain/derived/drafts/chain/input/parts/eye_r.png +0 -0
  287. package/templates/example-chain/derived/drafts/chain/input/parts/face.png +0 -0
  288. package/templates/example-chain/derived/drafts/chain/input/parts/lantern.png +0 -0
  289. package/templates/example-chain/derived/drafts/chain/input/parts/torso.png +0 -0
  290. package/templates/example-chain/derived/drafts/chain/input/parts.json +188 -0
  291. package/templates/example-chain/derived/drafts/chain/input/recomposite_rig.png +0 -0
  292. package/templates/example-chain/derived/drafts/chain/interview-requirements.json +8 -0
  293. package/templates/example-chain/derived/drafts/chain/out/check/build/run.json +40 -0
  294. package/templates/example-chain/derived/drafts/chain/out/check/build/skeleton.atlas +28 -0
  295. package/templates/example-chain/derived/drafts/chain/out/check/build/skeleton.json +674 -0
  296. package/templates/example-chain/derived/drafts/chain/out/check/build/skeleton.model.json +1072 -0
  297. package/templates/example-chain/derived/drafts/chain/out/check/build/skeleton.png +0 -0
  298. package/templates/example-chain/derived/drafts/chain/out/images/background.png +0 -0
  299. package/templates/example-chain/derived/drafts/chain/out/images/eye_l.png +0 -0
  300. package/templates/example-chain/derived/drafts/chain/out/images/eye_r.png +0 -0
  301. package/templates/example-chain/derived/drafts/chain/out/images/face.png +0 -0
  302. package/templates/example-chain/derived/drafts/chain/out/images/lantern.png +0 -0
  303. package/templates/example-chain/derived/drafts/chain/out/images/torso.png +0 -0
  304. package/templates/example-chain/derived/drafts/chain/out/mesh_report.json +15 -0
  305. package/templates/example-chain/derived/drafts/chain/out/motion.json +137 -0
  306. package/templates/example-chain/derived/drafts/chain/out/rig.json +536 -0
  307. package/templates/example-chain/derived/drafts/chain/out/run.json +52 -0
  308. package/templates/example-chain/derived/review/v1/config.json +88 -0
  309. package/templates/example-chain/derived/review/v1/interview-requirements.json +8 -0
  310. package/templates/example-chain/derived/review/v1/out/check/build/run.json +40 -0
  311. package/templates/example-chain/derived/review/v1/out/check/build/skeleton.atlas +28 -0
  312. package/templates/example-chain/derived/review/v1/out/check/build/skeleton.json +674 -0
  313. package/templates/example-chain/derived/review/v1/out/check/build/skeleton.model.json +1072 -0
  314. package/templates/example-chain/derived/review/v1/out/check/build/skeleton.png +0 -0
  315. package/templates/example-chain/derived/review/v1/out/parts/background.png +0 -0
  316. package/templates/example-chain/derived/review/v1/out/parts/eye_l.png +0 -0
  317. package/templates/example-chain/derived/review/v1/out/parts/eye_r.png +0 -0
  318. package/templates/example-chain/derived/review/v1/out/parts/face.png +0 -0
  319. package/templates/example-chain/derived/review/v1/out/parts/lantern.png +0 -0
  320. package/templates/example-chain/derived/review/v1/out/parts/torso.png +0 -0
  321. package/templates/example-chain/derived/review/v1/out/parts.json +188 -0
  322. package/templates/example-chain/derived/review/v1/out/rig/images/background.png +0 -0
  323. package/templates/example-chain/derived/review/v1/out/rig/images/eye_l.png +0 -0
  324. package/templates/example-chain/derived/review/v1/out/rig/images/eye_r.png +0 -0
  325. package/templates/example-chain/derived/review/v1/out/rig/images/face.png +0 -0
  326. package/templates/example-chain/derived/review/v1/out/rig/images/lantern.png +0 -0
  327. package/templates/example-chain/derived/review/v1/out/rig/images/torso.png +0 -0
  328. package/templates/example-chain/derived/review/v1/out/rig/mesh_report.json +15 -0
  329. package/templates/example-chain/derived/review/v1/out/rig/motion.json +137 -0
  330. package/templates/example-chain/derived/review/v1/out/rig/rig.json +536 -0
  331. package/templates/example-chain/derived/review/v1/out/rig/run.json +52 -0
  332. package/templates/example-chain/evidence/captions.json +1 -1
  333. package/templates/example-chain/evidence/direction/v2.json +1 -1
  334. package/templates/example-chain/evidence/direction-input.json +1 -1
  335. package/templates/example-chain/evidence/interview/camera_depth.json +2 -2
  336. package/templates/example-chain/evidence/interview/motion.json +1 -1
  337. package/templates/example-chain/evidence/interview/post_composition.json +2 -2
  338. package/templates/example-chain/evidence/interview/post_timing.json +2 -2
  339. package/templates/example-chain/evidence/interview/turn.json +108 -0
  340. package/templates/example-chain/evidence/scene/s1.json +2 -2
  341. package/templates/example-chain/outputs/v1/out/check/build/run.json +40 -0
  342. package/templates/example-chain/outputs/v1/out/check/build/skeleton.atlas +28 -0
  343. package/templates/example-chain/outputs/v1/out/check/build/skeleton.json +674 -0
  344. package/templates/example-chain/outputs/v1/out/check/build/skeleton.model.json +1072 -0
  345. package/templates/example-chain/outputs/v1/out/check/build/skeleton.png +0 -0
  346. package/templates/example-chain/outputs/v1/out/painting_in_rig.png +0 -0
  347. package/templates/example-chain/outputs/v1/out/parts/background.png +0 -0
  348. package/templates/example-chain/outputs/v1/out/parts/eye_l.png +0 -0
  349. package/templates/example-chain/outputs/v1/out/parts/eye_r.png +0 -0
  350. package/templates/example-chain/outputs/v1/out/parts/face.png +0 -0
  351. package/templates/example-chain/outputs/v1/out/parts/lantern.png +0 -0
  352. package/templates/example-chain/outputs/v1/out/parts/torso.png +0 -0
  353. package/templates/example-chain/outputs/v1/out/parts.json +188 -0
  354. package/templates/example-chain/outputs/v1/out/rig/images/background.png +0 -0
  355. package/templates/example-chain/outputs/v1/out/rig/images/eye_l.png +0 -0
  356. package/templates/example-chain/outputs/v1/out/rig/images/eye_r.png +0 -0
  357. package/templates/example-chain/outputs/v1/out/rig/images/face.png +0 -0
  358. package/templates/example-chain/outputs/v1/out/rig/images/lantern.png +0 -0
  359. package/templates/example-chain/outputs/v1/out/rig/images/torso.png +0 -0
  360. package/templates/example-chain/outputs/v1/out/rig/mesh_report.json +15 -0
  361. package/templates/example-chain/outputs/v1/out/rig/motion.json +137 -0
  362. package/templates/example-chain/outputs/v1/out/rig/rig.json +536 -0
  363. package/templates/example-chain/outputs/v1/out/rig/run.json +52 -0
  364. package/templates/example-chain/project.json +127 -78
  365. package/templates/example-direction/derived/review/v1/out/check/build/run.json +2 -2
  366. package/templates/example-draft/derived/drafts/lantern/out/check/build/run.json +2 -2
  367. package/templates/example-draft/derived/drafts/lantern/out/run.json +2 -2
  368. package/templates/example-draft/derived/review/v1/out/check/build/run.json +2 -2
  369. package/templates/example-draft/derived/review/v1/out/rig/run.json +2 -2
  370. package/templates/example-draft/derived/review/v2/out/check/build/run.json +2 -2
  371. package/templates/example-draft/derived/review/v2/out/rig/run.json +2 -2
  372. package/templates/example-draft/evidence/direction/v4.json +3 -3
  373. package/templates/example-draft/evidence/direction-input-v4.json +3 -3
  374. package/templates/example-draft/evidence/parts-review.json +1 -1
  375. package/templates/example-draft/evidence/review-reads/{52673b635b9cbc92da1a9e3d4c023a249bbacf79cd21b59724f03f18f8caf09b.json → 8866887a00012b5d5944b7525a478aea70931935bff35ddfcb186ffc515ef5af.json} +2 -2
  376. package/templates/example-draft/evidence/scene/s1.json +3 -3
  377. package/templates/example-draft/evidence/scene-citations.json +1 -1
  378. package/templates/example-draft/out/check/build/run.json +2 -2
  379. package/templates/example-draft/out/run.json +2 -2
  380. package/templates/example-draft/outputs/v1/out/check/build/run.json +2 -2
  381. package/templates/example-draft/outputs/v1/out/rig/run.json +2 -2
  382. package/templates/example-draft/outputs/v2/out/check/build/run.json +2 -2
  383. package/templates/example-draft/outputs/v2/out/rig/run.json +2 -2
  384. package/templates/example-interview-choice.json +113 -68
  385. package/templates/example-interview-options/constraint.json +2 -2
  386. package/templates/example-interview-options/example-assemble.json +2 -2
  387. package/templates/example-interview-options/example-bones.json +5 -5
  388. package/templates/example-interview-options/example-camera-depth.json +3 -3
  389. package/templates/example-interview-options/example-constraints.json +5 -5
  390. package/templates/example-interview-options/example-meshes.json +5 -5
  391. package/templates/example-interview-options/example-motion.json +5 -5
  392. package/templates/example-interview-options/example-picks.json +2 -2
  393. package/templates/example-interview-options/example-post-composition.json +3 -3
  394. package/templates/example-interview-options/example-post-timing.json +3 -3
  395. package/templates/example-interview-options/example-reference.json +2 -2
  396. package/templates/example-interview-options/example-rig-type.json +2 -2
  397. package/templates/example-interview-options/example-seethrough.json +2 -2
  398. package/templates/example-interview-options/example-turn.json +108 -0
  399. package/templates/example-interview-options/fullscreen.json +2 -2
  400. package/templates/example-interview-options/other-explicit.json +2 -2
  401. package/templates/example-interview-options/other-pending.json +2 -2
  402. package/templates/example-interview-options/other-picks.json +2 -2
  403. package/templates/example-invariants/invariants.json +223 -164
  404. package/templates/example-project.json +301 -249
  405. package/templates/example-remesh/.gitignore +6 -0
  406. package/templates/example-remesh/.npmignore +1 -0
  407. package/templates/example-remesh/AGENT-REFERENCE.md +62 -0
  408. package/templates/example-remesh/AGENTS.md +4 -26
  409. package/templates/example-remesh/derived/drafts/remesh/config.json +88 -0
  410. package/templates/example-remesh/derived/drafts/remesh/input/painting_in_rig.png +0 -0
  411. package/templates/example-remesh/derived/drafts/remesh/input/parts/background.png +0 -0
  412. package/templates/example-remesh/derived/drafts/remesh/input/parts/eye_l.png +0 -0
  413. package/templates/example-remesh/derived/drafts/remesh/input/parts/eye_r.png +0 -0
  414. package/templates/example-remesh/derived/drafts/remesh/input/parts/face.png +0 -0
  415. package/templates/example-remesh/derived/drafts/remesh/input/parts/lantern.png +0 -0
  416. package/templates/example-remesh/derived/drafts/remesh/input/parts/torso.png +0 -0
  417. package/templates/example-remesh/derived/drafts/remesh/input/parts.json +188 -0
  418. package/templates/example-remesh/derived/drafts/remesh/input/recomposite_rig.png +0 -0
  419. package/templates/example-remesh/derived/drafts/remesh/interview-requirements.json +8 -0
  420. package/templates/example-remesh/derived/drafts/remesh/out/check/build/run.json +40 -0
  421. package/templates/example-remesh/derived/drafts/remesh/out/check/build/skeleton.atlas +28 -0
  422. package/templates/example-remesh/derived/drafts/remesh/out/check/build/skeleton.json +674 -0
  423. package/templates/example-remesh/derived/drafts/remesh/out/check/build/skeleton.model.json +1072 -0
  424. package/templates/example-remesh/derived/drafts/remesh/out/check/build/skeleton.png +0 -0
  425. package/templates/example-remesh/derived/drafts/remesh/out/images/background.png +0 -0
  426. package/templates/example-remesh/derived/drafts/remesh/out/images/eye_l.png +0 -0
  427. package/templates/example-remesh/derived/drafts/remesh/out/images/eye_r.png +0 -0
  428. package/templates/example-remesh/derived/drafts/remesh/out/images/face.png +0 -0
  429. package/templates/example-remesh/derived/drafts/remesh/out/images/lantern.png +0 -0
  430. package/templates/example-remesh/derived/drafts/remesh/out/images/torso.png +0 -0
  431. package/templates/example-remesh/derived/drafts/remesh/out/mesh_report.json +15 -0
  432. package/templates/example-remesh/derived/drafts/remesh/out/motion.json +137 -0
  433. package/templates/example-remesh/derived/drafts/remesh/out/rig.json +536 -0
  434. package/templates/example-remesh/derived/drafts/remesh/out/run.json +52 -0
  435. package/templates/example-remesh/derived/review/v1/config.json +88 -0
  436. package/templates/example-remesh/derived/review/v1/interview-requirements.json +8 -0
  437. package/templates/example-remesh/derived/review/v1/out/check/build/run.json +44 -0
  438. package/templates/example-remesh/derived/review/v1/out/check/build/skeleton.atlas +28 -0
  439. package/templates/example-remesh/derived/review/v1/out/check/build/skeleton.json +674 -0
  440. package/templates/example-remesh/derived/review/v1/out/check/build/skeleton.model.json +1072 -0
  441. package/templates/example-remesh/derived/review/v1/out/check/build/skeleton.png +0 -0
  442. package/templates/example-remesh/derived/review/v1/out/painting_in_rig.png +0 -0
  443. package/templates/example-remesh/derived/review/v1/out/parts/background.png +0 -0
  444. package/templates/example-remesh/derived/review/v1/out/parts/eye_l.png +0 -0
  445. package/templates/example-remesh/derived/review/v1/out/parts/eye_r.png +0 -0
  446. package/templates/example-remesh/derived/review/v1/out/parts/face.png +0 -0
  447. package/templates/example-remesh/derived/review/v1/out/parts/lantern.png +0 -0
  448. package/templates/example-remesh/derived/review/v1/out/parts/torso.png +0 -0
  449. package/templates/example-remesh/derived/review/v1/out/parts.json +188 -0
  450. package/templates/example-remesh/derived/review/v1/out/rig/images/background.png +0 -0
  451. package/templates/example-remesh/derived/review/v1/out/rig/images/eye_l.png +0 -0
  452. package/templates/example-remesh/derived/review/v1/out/rig/images/eye_r.png +0 -0
  453. package/templates/example-remesh/derived/review/v1/out/rig/images/face.png +0 -0
  454. package/templates/example-remesh/derived/review/v1/out/rig/images/lantern.png +0 -0
  455. package/templates/example-remesh/derived/review/v1/out/rig/images/torso.png +0 -0
  456. package/templates/example-remesh/derived/review/v1/out/rig/mesh_report.json +15 -0
  457. package/templates/example-remesh/derived/review/v1/out/rig/motion.json +137 -0
  458. package/templates/example-remesh/derived/review/v1/out/rig/rig.json +536 -0
  459. package/templates/example-remesh/derived/review/v1/out/rig/run.json +52 -0
  460. package/templates/example-remesh/derived/review/v2/config.json +88 -0
  461. package/templates/example-remesh/derived/review/v2/interview-requirements.json +8 -0
  462. package/templates/example-remesh/derived/review/v2/out/check/build/run.json +44 -0
  463. package/templates/example-remesh/derived/review/v2/out/check/build/skeleton.atlas +28 -0
  464. package/templates/example-remesh/derived/review/v2/out/check/build/skeleton.json +674 -0
  465. package/templates/example-remesh/derived/review/v2/out/check/build/skeleton.model.json +1072 -0
  466. package/templates/example-remesh/derived/review/v2/out/check/build/skeleton.png +0 -0
  467. package/templates/example-remesh/derived/review/v2/out/painting_in_rig.png +0 -0
  468. package/templates/example-remesh/derived/review/v2/out/parts/background.png +0 -0
  469. package/templates/example-remesh/derived/review/v2/out/parts/eye_l.png +0 -0
  470. package/templates/example-remesh/derived/review/v2/out/parts/eye_r.png +0 -0
  471. package/templates/example-remesh/derived/review/v2/out/parts/face.png +0 -0
  472. package/templates/example-remesh/derived/review/v2/out/parts/lantern.png +0 -0
  473. package/templates/example-remesh/derived/review/v2/out/parts/torso.png +0 -0
  474. package/templates/example-remesh/derived/review/v2/out/parts.json +188 -0
  475. package/templates/example-remesh/derived/review/v2/out/rig/images/background.png +0 -0
  476. package/templates/example-remesh/derived/review/v2/out/rig/images/eye_l.png +0 -0
  477. package/templates/example-remesh/derived/review/v2/out/rig/images/eye_r.png +0 -0
  478. package/templates/example-remesh/derived/review/v2/out/rig/images/face.png +0 -0
  479. package/templates/example-remesh/derived/review/v2/out/rig/images/lantern.png +0 -0
  480. package/templates/example-remesh/derived/review/v2/out/rig/images/torso.png +0 -0
  481. package/templates/example-remesh/derived/review/v2/out/rig/mesh_report.json +15 -0
  482. package/templates/example-remesh/derived/review/v2/out/rig/motion.json +137 -0
  483. package/templates/example-remesh/derived/review/v2/out/rig/rig.json +536 -0
  484. package/templates/example-remesh/derived/review/v2/out/rig/run.json +52 -0
  485. package/templates/example-remesh/derived/review/v3/config.json +88 -0
  486. package/templates/example-remesh/derived/review/v3/interview-requirements.json +8 -0
  487. package/templates/example-remesh/derived/review/v3/out/check/build/run.json +44 -0
  488. package/templates/example-remesh/derived/review/v3/out/check/build/skeleton.atlas +28 -0
  489. package/templates/example-remesh/derived/review/v3/out/check/build/skeleton.json +674 -0
  490. package/templates/example-remesh/derived/review/v3/out/check/build/skeleton.model.json +1072 -0
  491. package/templates/example-remesh/derived/review/v3/out/check/build/skeleton.png +0 -0
  492. package/templates/example-remesh/derived/review/v3/out/painting_in_rig.png +0 -0
  493. package/templates/example-remesh/derived/review/v3/out/parts/background.png +0 -0
  494. package/templates/example-remesh/derived/review/v3/out/parts/eye_l.png +0 -0
  495. package/templates/example-remesh/derived/review/v3/out/parts/eye_r.png +0 -0
  496. package/templates/example-remesh/derived/review/v3/out/parts/face.png +0 -0
  497. package/templates/example-remesh/derived/review/v3/out/parts/lantern.png +0 -0
  498. package/templates/example-remesh/derived/review/v3/out/parts/torso.png +0 -0
  499. package/templates/example-remesh/derived/review/v3/out/parts.json +188 -0
  500. package/templates/example-remesh/derived/review/v3/out/rig/images/background.png +0 -0
  501. package/templates/example-remesh/derived/review/v3/out/rig/images/eye_l.png +0 -0
  502. package/templates/example-remesh/derived/review/v3/out/rig/images/eye_r.png +0 -0
  503. package/templates/example-remesh/derived/review/v3/out/rig/images/face.png +0 -0
  504. package/templates/example-remesh/derived/review/v3/out/rig/images/lantern.png +0 -0
  505. package/templates/example-remesh/derived/review/v3/out/rig/images/torso.png +0 -0
  506. package/templates/example-remesh/derived/review/v3/out/rig/mesh_report.json +15 -0
  507. package/templates/example-remesh/derived/review/v3/out/rig/motion.json +137 -0
  508. package/templates/example-remesh/derived/review/v3/out/rig/rig.json +536 -0
  509. package/templates/example-remesh/derived/review/v3/out/rig/run.json +52 -0
  510. package/templates/example-remesh/evidence/captions.json +1 -1
  511. package/templates/example-remesh/evidence/direction/v4.json +1 -1
  512. package/templates/example-remesh/evidence/direction/v5.json +1 -1
  513. package/templates/example-remesh/evidence/direction-input-v4.json +1 -1
  514. package/templates/example-remesh/evidence/direction-input-v5.json +1 -1
  515. package/templates/example-remesh/evidence/interview/assemble.json +1 -1
  516. package/templates/example-remesh/evidence/interview/bones.json +4 -4
  517. package/templates/example-remesh/evidence/interview/camera_depth.json +2 -2
  518. package/templates/example-remesh/evidence/interview/constraints.json +4 -4
  519. package/templates/example-remesh/evidence/interview/meshes.json +4 -4
  520. package/templates/example-remesh/evidence/interview/motion.json +4 -4
  521. package/templates/example-remesh/evidence/interview/post_composition.json +2 -2
  522. package/templates/example-remesh/evidence/interview/post_timing.json +2 -2
  523. package/templates/example-remesh/evidence/interview/reference.json +1 -1
  524. package/templates/example-remesh/evidence/interview/rig_type.json +1 -1
  525. package/templates/example-remesh/evidence/interview/seethrough.json +1 -1
  526. package/templates/example-remesh/evidence/interview/turn.json +108 -0
  527. package/templates/example-remesh/evidence/parts-review.json +1 -1
  528. package/templates/example-remesh/evidence/review-reads/{afe2522be4087f7f2fa7e97746d5f8574b0aedb474245ce2dbce0455e662f041.json → 99ade3eb19d70bd5f727138b3b4876ce86f1e1c22a59b516639bf85593996c32.json} +2 -2
  529. package/templates/example-remesh/evidence/scene/s1.json +2 -2
  530. package/templates/example-remesh/evidence/v1/registration/at-source.json +2 -2
  531. package/templates/example-remesh/evidence/v1/registration/rigc-run-1.json +2 -2
  532. package/templates/example-remesh/evidence/v2/registration/at-source.json +2 -2
  533. package/templates/example-remesh/evidence/v2/registration/rigc-run-1.json +2 -2
  534. package/templates/example-remesh/evidence/v3/registration/at-source.json +2 -2
  535. package/templates/example-remesh/evidence/v3/registration/rigc-run-1.json +2 -2
  536. package/templates/example-remesh/outputs/v1/idle/frames.json +29 -0
  537. package/templates/example-remesh/outputs/v1/idle/idle@1fps/contact.png +0 -0
  538. package/templates/example-remesh/outputs/v1/idle/idle@1fps/f0000.png +0 -0
  539. package/templates/example-remesh/outputs/v1/idle/idle@1fps/f0001.png +0 -0
  540. package/templates/example-remesh/outputs/v1/idle/idle@1fps/f0002.png +0 -0
  541. package/templates/example-remesh/outputs/v1/idle/idle@1fps/f0003.png +0 -0
  542. package/templates/example-remesh/outputs/v1/idle/idle@1fps/f0004.png +0 -0
  543. package/templates/example-remesh/outputs/v1/out/check/build/run.json +44 -0
  544. package/templates/example-remesh/outputs/v1/out/check/build/skeleton.atlas +28 -0
  545. package/templates/example-remesh/outputs/v1/out/check/build/skeleton.json +674 -0
  546. package/templates/example-remesh/outputs/v1/out/check/build/skeleton.model.json +1072 -0
  547. package/templates/example-remesh/outputs/v1/out/check/build/skeleton.png +0 -0
  548. package/templates/example-remesh/outputs/v1/out/painting_in_rig.png +0 -0
  549. package/templates/example-remesh/outputs/v1/out/parts/background.png +0 -0
  550. package/templates/example-remesh/outputs/v1/out/parts/eye_l.png +0 -0
  551. package/templates/example-remesh/outputs/v1/out/parts/eye_r.png +0 -0
  552. package/templates/example-remesh/outputs/v1/out/parts/face.png +0 -0
  553. package/templates/example-remesh/outputs/v1/out/parts/lantern.png +0 -0
  554. package/templates/example-remesh/outputs/v1/out/parts/torso.png +0 -0
  555. package/templates/example-remesh/outputs/v1/out/parts.json +188 -0
  556. package/templates/example-remesh/outputs/v1/out/rig/images/background.png +0 -0
  557. package/templates/example-remesh/outputs/v1/out/rig/images/eye_l.png +0 -0
  558. package/templates/example-remesh/outputs/v1/out/rig/images/eye_r.png +0 -0
  559. package/templates/example-remesh/outputs/v1/out/rig/images/face.png +0 -0
  560. package/templates/example-remesh/outputs/v1/out/rig/images/lantern.png +0 -0
  561. package/templates/example-remesh/outputs/v1/out/rig/images/torso.png +0 -0
  562. package/templates/example-remesh/outputs/v1/out/rig/mesh_report.json +15 -0
  563. package/templates/example-remesh/outputs/v1/out/rig/motion.json +137 -0
  564. package/templates/example-remesh/outputs/v1/out/rig/rig.json +536 -0
  565. package/templates/example-remesh/outputs/v1/out/rig/run.json +52 -0
  566. package/templates/example-remesh/outputs/v2/idle/frames.json +29 -0
  567. package/templates/example-remesh/outputs/v2/idle/idle@1fps/contact.png +0 -0
  568. package/templates/example-remesh/outputs/v2/idle/idle@1fps/f0000.png +0 -0
  569. package/templates/example-remesh/outputs/v2/idle/idle@1fps/f0001.png +0 -0
  570. package/templates/example-remesh/outputs/v2/idle/idle@1fps/f0002.png +0 -0
  571. package/templates/example-remesh/outputs/v2/idle/idle@1fps/f0003.png +0 -0
  572. package/templates/example-remesh/outputs/v2/idle/idle@1fps/f0004.png +0 -0
  573. package/templates/example-remesh/outputs/v2/out/check/build/run.json +44 -0
  574. package/templates/example-remesh/outputs/v2/out/check/build/skeleton.atlas +28 -0
  575. package/templates/example-remesh/outputs/v2/out/check/build/skeleton.json +674 -0
  576. package/templates/example-remesh/outputs/v2/out/check/build/skeleton.model.json +1072 -0
  577. package/templates/example-remesh/outputs/v2/out/check/build/skeleton.png +0 -0
  578. package/templates/example-remesh/outputs/v2/out/painting_in_rig.png +0 -0
  579. package/templates/example-remesh/outputs/v2/out/parts/background.png +0 -0
  580. package/templates/example-remesh/outputs/v2/out/parts/eye_l.png +0 -0
  581. package/templates/example-remesh/outputs/v2/out/parts/eye_r.png +0 -0
  582. package/templates/example-remesh/outputs/v2/out/parts/face.png +0 -0
  583. package/templates/example-remesh/outputs/v2/out/parts/lantern.png +0 -0
  584. package/templates/example-remesh/outputs/v2/out/parts/torso.png +0 -0
  585. package/templates/example-remesh/outputs/v2/out/parts.json +188 -0
  586. package/templates/example-remesh/outputs/v2/out/rig/images/background.png +0 -0
  587. package/templates/example-remesh/outputs/v2/out/rig/images/eye_l.png +0 -0
  588. package/templates/example-remesh/outputs/v2/out/rig/images/eye_r.png +0 -0
  589. package/templates/example-remesh/outputs/v2/out/rig/images/face.png +0 -0
  590. package/templates/example-remesh/outputs/v2/out/rig/images/lantern.png +0 -0
  591. package/templates/example-remesh/outputs/v2/out/rig/images/torso.png +0 -0
  592. package/templates/example-remesh/outputs/v2/out/rig/mesh_report.json +15 -0
  593. package/templates/example-remesh/outputs/v2/out/rig/motion.json +137 -0
  594. package/templates/example-remesh/outputs/v2/out/rig/rig.json +536 -0
  595. package/templates/example-remesh/outputs/v2/out/rig/run.json +52 -0
  596. package/templates/example-remesh/outputs/v3/idle/frames.json +29 -0
  597. package/templates/example-remesh/outputs/v3/idle/idle@1fps/contact.png +0 -0
  598. package/templates/example-remesh/outputs/v3/idle/idle@1fps/f0000.png +0 -0
  599. package/templates/example-remesh/outputs/v3/idle/idle@1fps/f0001.png +0 -0
  600. package/templates/example-remesh/outputs/v3/idle/idle@1fps/f0002.png +0 -0
  601. package/templates/example-remesh/outputs/v3/idle/idle@1fps/f0003.png +0 -0
  602. package/templates/example-remesh/outputs/v3/idle/idle@1fps/f0004.png +0 -0
  603. package/templates/example-remesh/outputs/v3/out/check/build/run.json +44 -0
  604. package/templates/example-remesh/outputs/v3/out/check/build/skeleton.atlas +28 -0
  605. package/templates/example-remesh/outputs/v3/out/check/build/skeleton.json +674 -0
  606. package/templates/example-remesh/outputs/v3/out/check/build/skeleton.model.json +1072 -0
  607. package/templates/example-remesh/outputs/v3/out/check/build/skeleton.png +0 -0
  608. package/templates/example-remesh/outputs/v3/out/painting_in_rig.png +0 -0
  609. package/templates/example-remesh/outputs/v3/out/parts/background.png +0 -0
  610. package/templates/example-remesh/outputs/v3/out/parts/eye_l.png +0 -0
  611. package/templates/example-remesh/outputs/v3/out/parts/eye_r.png +0 -0
  612. package/templates/example-remesh/outputs/v3/out/parts/face.png +0 -0
  613. package/templates/example-remesh/outputs/v3/out/parts/lantern.png +0 -0
  614. package/templates/example-remesh/outputs/v3/out/parts/torso.png +0 -0
  615. package/templates/example-remesh/outputs/v3/out/parts.json +188 -0
  616. package/templates/example-remesh/outputs/v3/out/rig/images/background.png +0 -0
  617. package/templates/example-remesh/outputs/v3/out/rig/images/eye_l.png +0 -0
  618. package/templates/example-remesh/outputs/v3/out/rig/images/eye_r.png +0 -0
  619. package/templates/example-remesh/outputs/v3/out/rig/images/face.png +0 -0
  620. package/templates/example-remesh/outputs/v3/out/rig/images/lantern.png +0 -0
  621. package/templates/example-remesh/outputs/v3/out/rig/images/torso.png +0 -0
  622. package/templates/example-remesh/outputs/v3/out/rig/mesh_report.json +15 -0
  623. package/templates/example-remesh/outputs/v3/out/rig/motion.json +137 -0
  624. package/templates/example-remesh/outputs/v3/out/rig/rig.json +536 -0
  625. package/templates/example-remesh/outputs/v3/out/rig/run.json +52 -0
  626. package/templates/example-remesh/project.json +279 -230
  627. package/templates/example-reopened/project.json +341 -289
  628. package/templates/example-split/captions.json +1 -1
  629. package/templates/project.json +10 -2
@@ -2,9 +2,8 @@ import { overrideFindings } from './overrides.ts';
2
2
  import { intakeItem, intakeWarnings, INTAKE_QUESTION } from './intake.ts';
3
3
  /** Public record writers; validation precedes every project write. */
4
4
  import { readFileSync, renameSync, writeFileSync } from 'node:fs';
5
- import { fileURLToPath } from 'node:url';
6
- import { acquireBoundJobWrite, jobRuntimeExecArgv } from './job-writer.ts';
7
- import { assertActSubjectDeclared, decodeReviewAct, reviewActFindings, reviewActTransport, type ReviewActTransport, type InterviewPicksAct } from './review-act.ts';
5
+ import { acquireBoundJobWrite } from './job-writer.ts';
6
+ import { assertActSubjectDeclared, decodeReviewAct, fetchReviewActBytes, reviewActFindings, reviewActTransport, type ReviewActTransport, type InterviewPicksAct } from './review-act.ts';
8
7
  import { dirname, join, resolve } from 'node:path';
9
8
  import { isDeepStrictEqual as equal } from 'node:util';
10
9
  import { asArray, checkProject, collectItems, collectSignoffs, isObject, loadCollection, collectionAnswer, type Item } from './check.ts';
@@ -155,17 +154,7 @@ export function interviewAnswerCommand(args: string[], now: number, hooks: Inter
155
154
  const invalid = reviewActFindings(next)[0];
156
155
  if (invalid) refuse(invalid.code,invalid.message);
157
156
  const transport: ReviewActTransport = { ...reviewActTransport(dir, opts.one['--from-review']), kind: 'interview-picks' };
158
- if (hooks.fetch) fetched = hooks.fetch(transport);
159
- else {
160
- const child = Bun.spawnSync([process.execPath, ...jobRuntimeExecArgv(), fileURLToPath(new URL('./review-act-runtime.ts', import.meta.url))], {
161
- stdin: Buffer.from(JSON.stringify(transport)), stdout: 'pipe', stderr: 'pipe',
162
- });
163
- let response: { ok: boolean; bytes: string; code: string; message: string };
164
- try { response = JSON.parse(child.stdout.toString()); }
165
- catch { return refuse('REVIEW_ACT_FETCH_REFUSED', 'project.json.review_acts: fetch adapter failed; restore the live Review job'); }
166
- if (!response.ok || child.exitCode) refuse(response.code, response.message);
167
- fetched = response;
168
- }
157
+ fetched = hooks.fetch ? hooks.fetch(transport) : { bytes: fetchReviewActBytes(transport) };
169
158
  act = decodeReviewAct(fetched.bytes, 'interview-picks');
170
159
  if (act.jobId !== transport.jobId || act.actId !== transport.actId || Date.parse(act.at) > now
171
160
  || act.value.questionId !== item.id) refuse('REVIEW_ACT_INVALID', `${item.path}: fetched identity, question or date differs; fetch the correct interview act`);
package/src/cli.ts CHANGED
@@ -85,6 +85,7 @@ ${INPUT_CLI} rig-create init <dir> [--no-install] create a project
85
85
  move to the next stage authorized by a live signoff
86
86
  rig-create step-status <step> <status> --by <who> [<project dir>] [--note-file <path>]
87
87
  record an audited forward step status
88
+ rig-create reopen <stage> [<project dir>] --from-review <actId>
88
89
  rig-create reopen <stage> --by <who> [--verbatim-file <file>] [--source <text>] [<project dir>]
89
90
  record a human backward move without deleting history
90
91
  rig-create pause --by <who> [--verbatim-file <file>] [<project dir>]
package/src/feedback.ts CHANGED
@@ -20,12 +20,11 @@ import { assertCorrectionApproval } from './correction-writers.ts';
20
20
  */
21
21
  import { closeSync, fsyncSync, mkdirSync, openSync, renameSync, rmSync, writeFileSync } from 'node:fs';
22
22
  import { existsSync, readFileSync } from './check-reads.ts';
23
- import { fileURLToPath } from 'node:url';
24
23
  import { randomUUID } from 'node:crypto';
25
24
  import { isDeepStrictEqual } from 'node:util';
26
- import { acquireBoundJobWrite, jobRuntimeExecArgv } from './job-writer.ts';
25
+ import { acquireBoundJobWrite } from './job-writer.ts';
27
26
  import { jobHash } from './job-outbox.ts';
28
- import { assertReviewActSource, decodeReviewAct, reviewActFindings, reviewActPublicationWarning, reviewActStill, reviewActTransport, reviewActVerbatim, reviewActVersionHash, type FetchedReviewAct } from './review-act.ts';
27
+ import { assertReviewActSource, decodeReviewAct, fetchReviewActRuntime, reviewActFindings, reviewActPublicationWarning, reviewActStill, reviewActTransport, reviewActVerbatim, reviewActVersionHash, type FetchedReviewAct } from './review-act.ts';
29
28
  import { newRefusals } from './refusal-baseline.ts';
30
29
  import { ingestedVerdictPairFinding } from './signoff-pair.ts';
31
30
  import { dirname, join, resolve } from 'node:path';
@@ -569,13 +568,7 @@ export function recordReviewAct(root: string, requestedVersion: number, actId: s
569
568
  root = resolve(root);
570
569
  const release = acquireBoundJobWrite(root);
571
570
  try {
572
- const child = Bun.spawnSync([process.execPath, ...jobRuntimeExecArgv(), fileURLToPath(new URL('./review-act-runtime.ts', import.meta.url))], {
573
- stdin: Buffer.from(JSON.stringify(reviewActTransport(root, actId))), stdout: 'pipe', stderr: 'pipe',
574
- });
575
- let response: { ok: boolean; bytes: string; stillBytes?: string; code: string; message: string };
576
- try { response = JSON.parse(child.stdout.toString()); }
577
- catch (error) { if (error instanceof Refusal) throw error; return refuse('REVIEW_ACT_FETCH_REFUSED', 'project.json.review_acts: fetch adapter failed; restore the live Review job'); }
578
- if (!response.ok || child.exitCode !== 0) refuse(response.code, response.message);
571
+ const response = fetchReviewActRuntime(reviewActTransport(root, actId));
579
572
  const act = decodeReviewAct(response.bytes);
580
573
  const selector = act.source.selector!;
581
574
  if (!('version' in selector) || selector.version !== requestedVersion) refuse('REVIEW_ACT_KIND_MISMATCH', 'project.json.review_acts: act names a different version; invoke review for the displayed version');
package/src/fix-ledger.ts CHANGED
@@ -148,7 +148,7 @@ const manifestOf = (draft: JsonObject): string | undefined => {
148
148
  return typeof entry?.sha256 === 'string' ? entry.sha256 : undefined;
149
149
  };
150
150
 
151
- const hasPartsReceipt = (project: JsonObject, id: string): boolean => asArray(project.review_acts)
151
+ export const hasPartsReceipt = (project: JsonObject, id: string): boolean => asArray(project.review_acts)
152
152
  .some(entry => isObject(entry) && entry.kind === 'parts-verdict' && entry.draft_id === id);
153
153
 
154
154
  /**
package/src/job-acts.ts CHANGED
@@ -75,6 +75,10 @@ export function pendingJobActs(project: JsonObject, acts: readonly ActV3[], root
75
75
  subject = `question ${act.value.questionId}`;
76
76
  args = ['interview-answer', act.value.questionId, root];
77
77
  break;
78
+ case 'reopen':
79
+ subject = `stage ${act.value.stage}`;
80
+ args = ['reopen', act.value.stage, root];
81
+ break;
78
82
  case 'signoff': {
79
83
  subject = `stage ${act.value.stage}`;
80
84
  args = ['signoff', act.value.stage, root];
@@ -0,0 +1,154 @@
1
+ /** Retire only a replaced project's demonstrably stopped managed host. Never edit Review's store. */
2
+ import { Database, constants as sqlite } from 'bun:sqlite';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { lstatSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, mkdirSync, writeFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { connect } from 'node:net';
7
+ import { jobHash, jobHostStateRoot, pendingPublications, privateJson } from './job-outbox.ts';
8
+ import { Refusal, refuse } from './refusal.ts';
9
+
10
+ interface StoredRoot { path: string; dev: number; ino: number }
11
+ interface ActiveJob { jobId: string; rootKey: string; root: StoredRoot; fingerprint: string }
12
+ interface Host { jobId: string; fingerprint: string; pid: number; origin: string }
13
+ const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/;
14
+
15
+ export function replacedRootRefusal(root: string, recorded?: { pid: number; origin: string }): never {
16
+ refuse('JOB_ROOT_REPLACED', `project.json: the retained Review host belongs to a replaced project directory at ${root}; stop the original job open host in its terminal${recorded ? ` (recorded PID ${recorded.pid}, ${recorded.origin})` : ''}, then retry rig-create job open or job reconcile. Recovery requires its recorded PID to be absent, its recorded port to refuse connections, no startup claim and no pending publication; restore missing or unsafe host metadata before retrying`);
17
+ }
18
+
19
+ function absent(path: string): boolean {
20
+ try { lstatSync(path); return false; }
21
+ catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true; throw error; }
22
+ }
23
+
24
+ function owned(path: string, directory: boolean): void {
25
+ const info = lstatSync(path);
26
+ if (info.isSymbolicLink() || !(directory ? info.isDirectory() : info.isFile())
27
+ || (info.mode & 0o077) || !directory && info.nlink !== 1
28
+ || process.getuid && info.uid !== process.getuid() || realpathSync(path) !== path) throw Error();
29
+ }
30
+
31
+ function dead(pid: number): boolean {
32
+ try { process.kill(pid, 0); return false; }
33
+ catch (error) { return (error as NodeJS.ErrnoException).code === 'ESRCH'; }
34
+ }
35
+
36
+ /** A timeout, reset or any other network error is uncertainty, never proof of a stopped listener. */
37
+ export function hostPortRefuses(port: number): Promise<boolean> {
38
+ return new Promise(resolve => {
39
+ const socket = connect({ host: '127.0.0.1', port });
40
+ const finish = (refused: boolean) => { socket.destroy(); resolve(refused); };
41
+ socket.setTimeout(500, () => finish(false));
42
+ socket.once('connect', () => finish(false));
43
+ socket.once('error', error => finish((error as NodeJS.ErrnoException).code === 'ECONNREFUSED'));
44
+ });
45
+ }
46
+
47
+ /** Caller holds acquireJobLock(root) through inspection, rename and host startup. */
48
+ export async function recoverReplacedJobHostState(
49
+ root: string,
50
+ now?: number,
51
+ probe: (port: number) => Promise<boolean> = hostPortRefuses,
52
+ ): Promise<string | undefined> {
53
+ const state = jobHostStateRoot(root), host = join(state, 'host'), file = join(host, 'jobs.sqlite');
54
+ if (absent(file)) return;
55
+ let claim: string | undefined, archived: string | undefined;
56
+ let claimIdentity: { dev: number; ino: number } | undefined;
57
+ let recorded: Host | undefined;
58
+ const nonce = randomUUID();
59
+ try {
60
+ // The only private store fields consumed are the active job's identity and root identity.
61
+ owned(state, true); owned(host, true); owned(file, false);
62
+ if (['-journal', '-wal', '-shm'].some(suffix => !absent(file + suffix))) throw Error();
63
+ const current = lstatSync(root);
64
+ const rootIdentity = { path: realpathSync(root), dev: current.dev, ino: current.ino };
65
+ const db = new Database(file, sqlite.SQLITE_OPEN_READONLY | sqlite.SQLITE_OPEN_NOFOLLOW);
66
+ let jobs: ActiveJob[];
67
+ try {
68
+ jobs = db.query<{ job_id: string; root_key: string; document: string }, []>(
69
+ 'SELECT job_id, root_key, document FROM jobs WHERE active=1',
70
+ ).all().map(row => {
71
+ const data = JSON.parse(row.document) as { root: StoredRoot; fingerprint: string };
72
+ return { jobId: row.job_id, rootKey: row.root_key, root: data.root, fingerprint: data.fingerprint };
73
+ });
74
+ } finally { db.close(); }
75
+ if (!jobs.length) return;
76
+ for (const job of jobs) {
77
+ if (!job.root || job.root.path !== rootIdentity.path || !Number.isSafeInteger(job.root.dev)
78
+ || job.root.dev < 0 || !Number.isSafeInteger(job.root.ino) || job.root.ino < 0) throw Error();
79
+ }
80
+ if (jobs.every(job => job.root.dev === current.dev && job.root.ino === current.ino)) return;
81
+ // Renaming the whole managed directory must not strand a different active incarnation.
82
+ if (jobs.length !== 1 || pendingPublications(root).length) throw Error();
83
+ const job = jobs[0]!;
84
+ if (!UUID.test(job.jobId) || job.fingerprint !== 'rig-create/live-review/1'
85
+ || job.rootKey !== jobHash(JSON.stringify([rootIdentity.path, job.fingerprint]))) throw Error();
86
+ const control = join(host, 'job-ui'); owned(control, true);
87
+ const claimNames = () => readdirSync(control).filter(name => name.endsWith('.claim') || name.endsWith('.claiming'));
88
+ if (claimNames().length) throw Error();
89
+ // Use Review's existing startup-claim protocol, so an overlapping listener cannot adopt the old job.
90
+ const candidate = join(control, `job-${job.jobId}.${nonce}.claiming`);
91
+ mkdirSync(candidate, { mode: 0o700 }); claim = candidate;
92
+ const installed = lstatSync(candidate); claimIdentity = { dev: installed.dev, ino: installed.ino };
93
+ writeFileSync(join(candidate, `owner.${nonce}.json`), JSON.stringify({ pid: process.pid, nonce }), { mode: 0o600, flag: 'wx' });
94
+ const target = join(control, `job-${job.jobId}.claim`);
95
+ if (!absent(target)) throw Error();
96
+ renameSync(candidate, target); claim = target;
97
+ const snapshot = (): { seal: string; hosts: Host[] } => {
98
+ owned(state, true); owned(host, true); owned(file, false); owned(control, true);
99
+ const currentRoot = lstatSync(root);
100
+ if (currentRoot.dev !== rootIdentity.dev || currentRoot.ino !== rootIdentity.ino || realpathSync(root) !== root) throw Error();
101
+ if (['-journal', '-wal', '-shm'].some(suffix => !absent(file + suffix))) throw Error();
102
+ const claims = claimNames();
103
+ if (claims.length !== 1 || join(control, claims[0]!) !== claim) throw Error();
104
+ const names = readdirSync(control).filter(name => name.endsWith('.json')).sort();
105
+ if (!names.includes(`${job.jobId}.json`)) throw Error();
106
+ const records = names.map(name => {
107
+ const path = join(control, name); owned(path, false);
108
+ const value = privateJson(path) as Host & { schema: string };
109
+ if (value.schema !== 'rig-review-job-host/1' || !UUID.test(value.jobId) || name !== `${value.jobId}.json`
110
+ || value.fingerprint !== job.fingerprint || !Number.isSafeInteger(value.pid) || value.pid <= 0
111
+ || !/^http:\/\/127\.0\.0\.1:([1-9][0-9]{0,4})$/.test(value.origin)
112
+ || Number(new URL(value.origin).port || 80) > 65535) throw Error();
113
+ return { path, value };
114
+ });
115
+ owned(claim!, true); owned(join(claim!, `owner.${nonce}.json`), false);
116
+ const paths = [root, state, host, control, file, claim!, join(claim!, `owner.${nonce}.json`), ...records.map(record => record.path)];
117
+ const seal = jobHash(JSON.stringify(paths.map(path => {
118
+ const info = lstatSync(path);
119
+ return [path, info.dev, info.ino, info.isFile() ? jobHash(readFileSync(path)) : null];
120
+ })));
121
+ return { seal, hosts: records.map(record => record.value) };
122
+ };
123
+ const before = snapshot();
124
+ recorded = before.hosts.find(record => record.jobId === job.jobId);
125
+ for (const record of before.hosts) {
126
+ if (!dead(record.pid) || !await probe(Number(new URL(record.origin).port || 80))) throw Error();
127
+ }
128
+ // No await between final identity/claim/PID recheck and rename. Keep all original state bytes.
129
+ if (snapshot().seal !== before.seal || before.hosts.some(record => !dead(record.pid))) throw Error();
130
+ const destination = `${state}.replaced-${new Date(now ?? current.mtimeMs).toISOString().replace(/[:.]/g, '-')}-${nonce}`;
131
+ if (!absent(destination)) throw Error();
132
+ renameSync(state, destination); archived = destination;
133
+ return destination;
134
+ } catch (error) {
135
+ if (error instanceof Refusal && error.code !== 'JOB_ROOT_REPLACED') throw error;
136
+ replacedRootRefusal(root, recorded);
137
+ } finally {
138
+ if (claim) {
139
+ const actual = archived ? join(archived, 'host', 'job-ui', claim.slice(claim.lastIndexOf('/') + 1)) : claim;
140
+ // This marker was created here; never remove a pre-existing Review claim or state directory.
141
+ try {
142
+ const info = lstatSync(actual);
143
+ if (!claimIdentity || !info.isDirectory() || info.isSymbolicLink() || info.dev !== claimIdentity.dev || info.ino !== claimIdentity.ino) throw Error();
144
+ const owner = join(actual, `owner.${nonce}.json`);
145
+ if (!absent(owner)) {
146
+ if (readFileSync(owner, 'utf8') !== JSON.stringify({ pid: process.pid, nonce })) throw Error();
147
+ rmSync(owner);
148
+ }
149
+ // rmdir refuses a competing owner's nonempty replacement.
150
+ rmdirSync(actual);
151
+ } catch { replacedRootRefusal(root, recorded); }
152
+ }
153
+ }
154
+ }
package/src/job-host.ts CHANGED
@@ -2,17 +2,19 @@ import { randomUUID } from 'node:crypto';
2
2
  import { readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { isDeepStrictEqual } from 'node:util';
5
- import { startJobHost, type JobHostAttachFailure, type JobHostOptions, type JobHostReuseBinding, type JobOwnerHost, type JobReviewerBinding, type JobSelector, type JobSubject, type JobEventV3, type ActV3 } from '@rig-c/review/job-host';
5
+ import { startJobHost, type JobHostAttachFailure, type JobHostOptions, type JobHostReuseBinding, type JobOwnerHost, type JobReviewerBinding, type JobSelector, type JobEventV3, type ActV3 } from '@rig-c/review/job-host';
6
6
  import { asArray, checkProject, collectItems, loadCollection, isObject, type Finding } from './check.ts';
7
7
  import { baselineOf, newFindings } from './refusal-baseline.ts';
8
8
  import { Refusal, refuse } from './refusal.ts';
9
9
  import { liveManifestPath, draftPublicationContext, currentJobIdentity, partsEnumerator, prepareDraftManifest, prepareLiveManifest, publicationFile, publicationSelector, readPublished, readRecordPublished } from './job-publication.ts';
10
- import { acknowledgePublication, acquireJobLock, assertJobIdentity, enqueuePublication, ensureJobPrivateIgnores, failPublication, JOB_BINDING_FILE, jobHash, jobHostStateRoot, pendingPublications, privateDirectory, publicationVersionHash, readAcknowledgedPublication, readWriterBinding, reconcilePublication, writePrivateJson, isRecordResult, type PublicationResult, type JobIdentity, type PendingPublication, type WriterBinding } from './job-outbox.ts';
10
+ import { acknowledgePublication, acquireJobLock, assertJobIdentity, enqueuePublication, ensureJobPrivateIgnores, failPublication, JOB_BINDING_FILE, jobHash, jobHostStateRoot, pendingPublications, privateDirectory, publicationVersionHash, readAcknowledgedPublication, readWriterBinding, reconcilePublication, writePrivateJson, isRecordResult, type PublicationResult, type JobIdentity, type PendingPublication, type WriterBinding, type PublicationSubject } from './job-outbox.ts';
11
11
  import { draftPublicationResult } from './job-writer.ts';
12
12
  import { attachCreateJobForClose } from './job-close.ts';
13
13
  import { pendingJobActs, type PendingJobAct } from './job-acts.ts';
14
14
  import { checkReviewPackages } from './review-package.ts';
15
+ import { reviewVerdictSignable } from './signable.ts';
15
16
  import type { JsonObject } from './templates.ts';
17
+ import { recoverReplacedJobHostState, replacedRootRefusal } from './job-host-state.ts';
16
18
 
17
19
  function installBinding(root: string, binding: WriterBinding): void {
18
20
  ensureJobPrivateIgnores(root);
@@ -79,10 +81,23 @@ export async function openCreateJob(
79
81
  if (existing) assertJobIdentity(existing.identity, identity);
80
82
  privateDirectory(identity.stateDir);
81
83
  let writerGrant: WriterGrant | undefined;
82
- const host = await startHost({
84
+ const start = () => startHost({
83
85
  ...options(identity, true, undefined, grant => { writerGrant = grant; }),
84
86
  ...(existing ? { port: Number(new URL(existing.origin).port) } : {}),
85
87
  });
88
+ let host: JobOwnerHost;
89
+ try {
90
+ host = await start();
91
+ } catch (error) {
92
+ if (typeof error !== 'object' || error === null || (error as { code?: unknown }).code !== 'JOB_ROOT_REPLACED') throw error;
93
+ if (identity.stateDir !== join(jobHostStateRoot(root), 'host') || !await recoverReplacedJobHostState(root, now)) replacedRootRefusal(root);
94
+ privateDirectory(identity.stateDir);
95
+ try { host = await start(); }
96
+ catch (retryError) {
97
+ if (typeof retryError === 'object' && retryError !== null && (retryError as { code?: unknown }).code === 'JOB_ROOT_REPLACED') replacedRootRefusal(root);
98
+ throw retryError;
99
+ }
100
+ }
86
101
  const file = join(root, 'project.json');
87
102
  const temporary = join(root, '.rig-create-job-record.tmp');
88
103
  try {
@@ -174,7 +189,7 @@ export async function publishPending(
174
189
  const currentHash = jobHash(bytes);
175
190
  if (currentHash !== entry.recordHash) { entry.recordHash = currentHash; delete entry.generation; }
176
191
  const project = JSON.parse(bytes.toString('utf8'));
177
- let subjects: readonly JobSubject[];
192
+ let subjects: readonly PublicationSubject[];
178
193
  if (isRecordResult(entry.result)) {
179
194
  subjects = readRecordPublished({ root: entry.root, record: { path: 'project.json', sha256: entry.recordHash }, result: entry.result, phase: 'inventory' }).subjects;
180
195
  } else {
@@ -216,7 +231,7 @@ export async function publishPending(
216
231
  }
217
232
 
218
233
  /** One declared subject after the ask step: asked now, asked before on this publication, or refused by Review. */
219
- export type AskOutcome = { readonly subject: JobSubject; readonly status: 'asked' | 'already' } | { readonly subject: JobSubject; readonly status: 'refused'; readonly code: string };
234
+ export type AskOutcome = { readonly subject: PublicationSubject; readonly status: 'asked' | 'already' } | { readonly subject: PublicationSubject; readonly status: 'refused'; readonly code: string };
220
235
 
221
236
  type AgentReply = { ok?: boolean; error?: { code?: string }; value?: { events?: unknown; status?: unknown; cursor?: unknown } };
222
237
 
@@ -251,10 +266,10 @@ async function* jobEvents(host: JobOwnerHost, strict = false): AsyncGenerator<Jo
251
266
  }
252
267
 
253
268
  /** The subjects already asked on this publication, from the same event reader as job acts. */
254
- async function askedSubjects(host: JobOwnerHost, publicationId: string): Promise<JobSubject[]> {
255
- const asked: JobSubject[] = [];
269
+ async function askedSubjects(host: JobOwnerHost, publicationId: string): Promise<PublicationSubject[]> {
270
+ const asked: PublicationSubject[] = [];
256
271
  for await (const event of jobEvents(host)) {
257
- if (event.type === 'pending' && event.ask.publicationId === publicationId) asked.push(event.ask.subject as JobSubject);
272
+ if (event.type === 'pending' && event.ask.publicationId === publicationId) asked.push(event.ask.subject as PublicationSubject);
258
273
  }
259
274
  return asked;
260
275
  }
@@ -282,7 +297,7 @@ export async function listCreateJobActs(
282
297
  * publication, pending or answered, is not asked again, so a re-open never shows a second
283
298
  * control. Asks stay limited to the declared list (`REVIEW_ACT_SUBJECT_UNDECLARED`).
284
299
  */
285
- export async function askDeclaredSubjects(host: JobOwnerHost, publicationId: string, subjects: readonly JobSubject[]): Promise<AskOutcome[]> {
300
+ export async function askDeclaredSubjects(host: JobOwnerHost, publicationId: string, subjects: readonly PublicationSubject[]): Promise<AskOutcome[]> {
286
301
  const before = await askedSubjects(host, publicationId);
287
302
  const outcomes: AskOutcome[] = [];
288
303
  for (const subject of subjects) {
@@ -298,7 +313,7 @@ export async function askDeclaredSubjects(host: JobOwnerHost, publicationId: str
298
313
  return outcomes;
299
314
  }
300
315
 
301
- export function subjectLabel(subject: JobSubject): string {
316
+ export function subjectLabel(subject: PublicationSubject): string {
302
317
  switch (subject.kind) {
303
318
  case 'parts-verdict': return `parts-verdict ${subject.draftId}`;
304
319
  case 'checkpoint': return `checkpoint ${subject.checkpointId}`;
@@ -325,11 +340,16 @@ export function refuseAskOutcomes(outcomes: readonly AskOutcome[]): void {
325
340
  refuse('JOB_ASK_REFUSED', `project.json: the publication is acknowledged but review refused the ask for ${named}; restore the published record bytes and run rig-create job open to ask again`);
326
341
  }
327
342
 
328
- export async function reconcileCreateJob(root: string, publish: (entry: PendingPublication) => Promise<unknown> = publishPending): Promise<number> {
343
+ export async function reconcileCreateJob(root: string, publish: (entry: PendingPublication) => Promise<unknown> = publishPending, now?: number): Promise<number> {
344
+ root = realpathSync(root);
329
345
  const release = acquireJobLock(root)!;
330
346
  let outcomes: AskOutcome[];
331
347
  let count: number;
332
- try { ({ count, outcomes } = await reconcileLocked(root, publish)); }
348
+ try {
349
+ const binding = readWriterBinding(root);
350
+ if (!binding || binding.identity.stateDir === join(jobHostStateRoot(root), 'host')) await recoverReplacedJobHostState(root, now);
351
+ ({ count, outcomes } = await reconcileLocked(root, publish));
352
+ }
333
353
  finally { release(); }
334
354
  refuseAskOutcomes(outcomes);
335
355
  return count;
@@ -482,13 +502,16 @@ export async function openDraftPublication(root: string, host: JobOwnerHost, now
482
502
  ? headSelector
483
503
  : undefined);
484
504
  const warnings: Finding[] = [];
505
+ const project = JSON.parse(bytes.toString('utf8')) as JsonObject;
485
506
  const draftContext = targetSelector?.kind === 'draft'
486
- ? draftPublicationContext(root, JSON.parse(bytes.toString('utf8')), targetSelector.id, warnings)
507
+ ? draftPublicationContext(root, project, targetSelector.id, warnings)
487
508
  : undefined;
488
509
  // Re-publish an older post head that still declares a sign-off or checkpoint:
489
510
  // either pending ask would prevent the legacy direction panel from rendering.
490
511
  const currentSubjects = draftContext?.subjects ?? (targetSelector?.kind === 'post'
491
- ? [{ kind: 'review-verdict' as const, stage: 'direction-review' as const }]
512
+ ? (reviewVerdictSignable(project, targetSelector.version, 'direction-review')
513
+ ? [{ kind: 'review-verdict' as const, stage: 'direction-review' as const }]
514
+ : [])
492
515
  : undefined);
493
516
  const reuseHead = Boolean(head && headSelector && source?.projectHash === jobHash(bytes) && (
494
517
  targetSelector === undefined || isDeepStrictEqual(headSelector, targetSelector)
package/src/job-outbox.ts CHANGED
@@ -4,7 +4,9 @@ import { existsSync, JOB_PRIVATE_DIRECTORY, lstatSync, readFileSync, readdirSync
4
4
  import { basename, dirname, join, resolve } from 'node:path';
5
5
  import { tmpdir } from 'node:os';
6
6
  import { isDeepStrictEqual } from 'node:util';
7
- import type { JobPublishRequest, JobReviewerBinding, JobSubject, RecordResult } from '@rig-c/review/job-host';
7
+ import type { JobPublishRequest, JobReviewerBinding, JobSubject, RecordSubject, RecordResult } from '@rig-c/review/job-host';
8
+
9
+ export type PublicationSubject = JobSubject | RecordSubject;
8
10
  import { refuse } from './refusal.ts';
9
11
 
10
12
  export const JOB_BINDING_FILE = '.rig-create-review-binding.json';
@@ -342,7 +344,7 @@ export interface AcknowledgedPublication {
342
344
  result: PendingPublication['result'];
343
345
  versionHash: string;
344
346
  /** The subjects the published context declared. Receipts written before they were retained omit it. */
345
- subjects?: readonly JobSubject[];
347
+ subjects?: readonly PublicationSubject[];
346
348
  }
347
349
 
348
350
  function acknowledgedPath(root: string, jobId: string, publicationId: string): string {
@@ -350,7 +352,7 @@ function acknowledgedPath(root: string, jobId: string, publicationId: string): s
350
352
  }
351
353
 
352
354
  /** Persist the version identity before the pending publication is removed. */
353
- export function acknowledgePublication(entry: PendingPublication, publicationId: string, generation: number, requestId: string, subjects?: readonly JobSubject[]): void {
355
+ export function acknowledgePublication(entry: PendingPublication, publicationId: string, generation: number, requestId: string, subjects?: readonly PublicationSubject[]): void {
354
356
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(publicationId) || !Number.isSafeInteger(generation) || generation < 0 || !entry.versionHash) {
355
357
  refuse('JOB_OUTBOX_INVALID', 'project.json: publication acknowledgment lacks its identity; reconcile the original pending publication');
356
358
  }
@@ -1,13 +1,13 @@
1
1
  import { checkInputs } from './inputs.ts';
2
2
  import { pngDimensions } from './scene.ts';
3
- import { checkpointRequired } from './plan-history.ts';
3
+ import { hasPartsReceipt } from './fix-ledger.ts';
4
4
  import { renameSync, rmSync, writeFileSync } from 'node:fs';
5
5
  import { existsSync, lstatSync, readFileSync, realpathSync } from './check-reads.ts';
6
6
  import { dirname, join, relative, resolve } from 'node:path';
7
7
  import { isDeepStrictEqual } from 'node:util';
8
8
  import { partsFileReferences } from '@rig-c/parts/src/parts.ts';
9
9
  import partsPackage from '@rig-c/parts/package.json';
10
- import { JOB_ACT_SCHEMA, JOB_API_SCHEMA, type JobPublicationReader, type JobPublishedContext, type JobSelector, type JobRecordSignoffStage } from '@rig-c/review/job-host';
10
+ import { JOB_ACT_SCHEMA, JOB_API_SCHEMA, type JobPublicationReader, type JobPublishedContext, type JobSelector, type JobRecordSignoffStage, type RecordPlanSubject } from '@rig-c/review/job-host';
11
11
  import { stepHash } from './step-hash.ts';
12
12
  import { checkpointEvidenceContext, type CheckpointEvidence } from './checkpoint-evidence.ts';
13
13
  import { locateParts, PARTS_PACKAGE } from './parts-sections.ts';
@@ -24,10 +24,10 @@ import {
24
24
  import { optionContextFiles, optionFile, validateContext } from './choices.ts';
25
25
  import { hasStageOrder, interviewStageOfGuide, stageOfStep, stepCoveredByApproval } from './stages.ts';
26
26
  import { selectAnswerHashes } from './signoff-answers.ts';
27
- import { assertInterviewRecordSignable, packageSignSubjects, stageSignable } from './signable.ts';
28
- import { collectReopens, liveSignoff } from './reopen.ts';
27
+ import { assertInterviewRecordSignable, checkpointSignable, packageSignSubjects, reviewVerdictSignable, stageSignable } from './signable.ts';
28
+ import { collectReopens, directionReviewClosed, liveSignoff } from './reopen.ts';
29
29
  import { acceptedReviewVerdict } from './review.ts';
30
- import { requireRecordDirectionTarget } from './record-direction-target.ts';
30
+ import { recordDirectionTarget, requireRecordDirectionTarget } from './record-direction-target.ts';
31
31
  import { latestPartsApproval, riggingContext } from './structure.ts';
32
32
  import { jobHash, pendingPublications, isRecordResult, type JobIdentity } from './job-outbox.ts';
33
33
  import { Refusal, refuse } from './refusal.ts';
@@ -57,8 +57,6 @@ type RecordContextBase = {
57
57
  readonly files: JobPublishedContext['files'];
58
58
  };
59
59
  type RecordSignoffStage = JobRecordSignoffStage;
60
- type RecordPlanSubject = { readonly kind: 'checkpoint'; readonly checkpointId: string }
61
- | { readonly kind: 'signoff'; readonly stage: RecordSignoffStage };
62
60
  export type RecordOptionContext = RecordContextBase & {
63
61
  readonly plan: null;
64
62
  readonly subjects: readonly [{ readonly kind: 'interview-picks'; readonly questionId: string; readonly setId: string }];
@@ -285,7 +283,9 @@ export function readRecordPublished({ root, record, result, phase, parts: sealed
285
283
  label = `option-set ${result.questionId} / ${result.setId}`;
286
284
  } else if (result.kind === 'plan') {
287
285
  if (!plan) throw Error('the requested plan is absent');
288
- if (stage === 'direction-review') {
286
+ const reopenDirection = stage === 'direction-review' && directionReviewClosed(project)
287
+ && recordDirectionTarget(project, now) === null;
288
+ if (stage === 'direction-review' && !reopenDirection) {
289
289
  const reopens = collectReopens(project, []);
290
290
  const post = asArray(project.versions).filter(isObject).filter(version => version.kind === 'post'
291
291
  && liveSignoff(reopens, 'post', Date.parse(String(version.at)))).at(-1);
@@ -297,7 +297,7 @@ export function readRecordPublished({ root, record, result, phase, parts: sealed
297
297
  requireRecordDirectionTarget(project, now);
298
298
  }
299
299
  const covered = steps.filter(step => stepCoveredByApproval(project, step, stage));
300
- const signable = stage !== null && ['interview', 'plan-approved', 'rigging-interview', 'post-interview', 'direction-review'].includes(stage)
300
+ const signable = !reopenDirection && stage !== null && ['interview', 'plan-approved', 'rigging-interview', 'post-interview', 'direction-review'].includes(stage)
301
301
  && (stage !== 'interview' || interviewSignoff) && stageSignable(project, stage);
302
302
  if (signable && stage === 'direction-review' && steps.length === 0) throw Error('direction-review record sign-off needs displayed plan steps');
303
303
  const emptyPost = stage === 'post-interview' && !steps.some(step => step.phase === 'post') && postNotPassed({ items });
@@ -311,9 +311,10 @@ export function readRecordPublished({ root, record, result, phase, parts: sealed
311
311
  }
312
312
  subjects = steps.filter(step => {
313
313
  if (step.status !== 'at-checkpoint' || (step.phase !== 'rig' && step.phase !== 'post') || stageOfStep(step.phase, String(step.id)) !== stage) return false;
314
- return checkpointRequired(project, step, now);
314
+ return checkpointSignable(project, stage, String(step.id), now);
315
315
  }).map(step => ({ kind: 'checkpoint' as const, checkpointId: String(step.id) }));
316
316
  if (signable) subjects = [...subjects, { kind: 'signoff', stage: stage as RecordSignoffStage }];
317
+ if (reopenDirection) subjects = [{ kind: 'reopen', stage: 'direction-review' }];
317
318
  label = `plan / ${stage}`;
318
319
  } else throw Error('unknown record result');
319
320
  // Detect record changes during closure reads as well as changes before entry.
@@ -513,9 +514,12 @@ export const readPublished: JobPublicationReader = async ({ root, record, result
513
514
  }
514
515
  const plan = project.plan as JsonObject | undefined;
515
516
  const steps = Array.isArray(plan?.steps) ? plan.steps as JsonObject[] : [];
516
- const subjects: JobPublishedContext['subjects'] = [{ kind: 'review-verdict', stage: selector.kind === 'post' ? 'direction-review' : 'rig-review' },
517
+ const reviewStage = selector.kind === 'post' ? 'direction-review' as const : 'rig-review' as const;
518
+ const subjects: JobPublishedContext['subjects'] = [
519
+ ...(reviewVerdictSignable(project, selector.version, reviewStage) ? [{ kind: 'review-verdict' as const, stage: reviewStage }] : []),
517
520
  // The legacy direction panel accepts only the verdict on a post publication.
518
- ...(selector.kind === 'post' ? [] : packageSignSubjects(project, effectiveStage(root, project), selector, steps.length))];
521
+ ...(selector.kind === 'post' ? [] : packageSignSubjects(project, effectiveStage(root, project), selector, steps.length)),
522
+ ];
519
523
  return {
520
524
  selector, plan: { steps: steps.map(step => ({ id: String(step.id), hash: stepHash(step) })) },
521
525
  parts: { enumeratorVersion: partsEnumerator.version, manifest: { path: manifestPath, sha256: jobHash(manifest) }, images },
@@ -576,7 +580,7 @@ export function draftPublishedContext(root: string, project: JsonObject, id: str
576
580
  const plan = project.plan as JsonObject | undefined;
577
581
  return { selector: { kind: 'draft', id }, plan: plan ? { steps: (plan.steps as JsonObject[] ?? []).map(step => ({ id: String(step.id), hash: stepHash(step) })) } : null,
578
582
  parts: { enumeratorVersion: partsEnumerator.version, manifest: { path: manifestPath, sha256: jobHash(manifest) }, images },
579
- subjects: [{ kind: 'parts-verdict', draftId: id }],
583
+ subjects: hasPartsReceipt(project, id) ? [] : [{ kind: 'parts-verdict', draftId: id }],
580
584
  partIds: (JSON.parse(manifest.toString('utf8')).parts as { name: string }[]).map(part => part.name), files };
581
585
  }
582
586
 
@@ -5,6 +5,16 @@ import { askOutcomeLines, assertRequestedDraft, assertRequestedRig, closeCreateJ
5
5
  import { failPublication, pendingPublications, reconcilePublication } from './job-outbox.ts';
6
6
  import { jobActsText } from './job-acts.ts';
7
7
  import { Refusal, refuse } from './refusal.ts';
8
+ import { replacedRootRefusal } from './job-host-state.ts';
9
+
10
+ /** Preserve this known producer refusal without printing an uncontrolled producer message. */
11
+ export function publicationFailure(error: unknown, root: string): Refusal | undefined {
12
+ if (error instanceof Refusal) return error;
13
+ if (typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'JOB_ROOT_REPLACED') {
14
+ try { replacedRootRefusal(root); } catch (failure) { return failure as Refusal; }
15
+ }
16
+ return undefined;
17
+ }
8
18
 
9
19
  async function run(): Promise<void> {
10
20
  const input = JSON.parse(readFileSync(0, 'utf8')) as { root: string; id?: string; reviewer?: string; role?: 'owner' | 'delegate'; draft?: string; rig?: string; question?: string; plan?: boolean; now: number; json?: boolean };
@@ -16,7 +26,8 @@ async function run(): Promise<void> {
16
26
  let outcomes: AskOutcome[] = [];
17
27
  try { await reconcilePublication(root, entry, async item => { outcomes = await publishPending(item); }); }
18
28
  catch (error) {
19
- const failure = error instanceof Refusal ? { code: error.code, message: error.message }
29
+ const named = publicationFailure(error, root);
30
+ const failure = named ? { code: named.code, message: named.message }
20
31
  : { code: 'JOB_PUBLISH_PENDING', message: 'project.json: host publication failed; restore the bound host before rig-create job reconcile' };
21
32
  failPublication(root, entry, failure);
22
33
  throw new Refusal('JOB_PUBLISH_PENDING', 'project.json: committed publication is pending; run rig-create job reconcile');
@@ -31,7 +42,7 @@ async function run(): Promise<void> {
31
42
  console.log(input.json ? JSON.stringify(result) : jobActsText(result));
32
43
  break;
33
44
  }
34
- case 'reconcile': console.log(`job: reconciled ${await reconcileCreateJob(root)} publication(s)`); break;
45
+ case 'reconcile': console.log(`job: reconciled ${await reconcileCreateJob(root, undefined, input.now)} publication(s)`); break;
35
46
  case 'close': await closeCreateJob(root); console.log('job: closed'); break;
36
47
  case 'open': {
37
48
  if (!input.reviewer || !input.role) refuse('JOB_BINDING_INVALID', 'project.json: job open needs --reviewer and --role owner|delegate');
@@ -64,7 +75,8 @@ async function run(): Promise<void> {
64
75
  host.stop();
65
76
  throw error;
66
77
  }
67
- console.error(error instanceof Refusal ? `${error.code}: ${error.message}` : 'JOB_PUBLISH_PENDING: project.json: draft publication failed; run rig-create job reconcile');
78
+ const named = publicationFailure(error, root);
79
+ console.error(named ? `${named.code}: ${named.message}` : 'JOB_PUBLISH_PENDING: project.json: draft publication failed; run rig-create job reconcile');
68
80
  process.exitCode = 1;
69
81
  }
70
82
  if (host.reused || stopping) {
@@ -93,7 +105,8 @@ if (import.meta.main) {
93
105
  try { await run(); }
94
106
  catch (error) {
95
107
  // Unexpected errors may embed producer input. Only controlled refusals leave this process.
96
- console.error(error instanceof Refusal ? `${error.code}: ${error.message}` : 'JOB_PUBLISH_PENDING: project.json: publication operation failed; run rig-create job reconcile');
108
+ const named = publicationFailure(error, 'the requested project directory');
109
+ console.error(named ? `${named.code}: ${named.message}` : 'JOB_PUBLISH_PENDING: project.json: publication operation failed; run rig-create job reconcile');
97
110
  process.exitCode = 1;
98
111
  }
99
112
  }