rig-create 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (555) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -2
  3. package/docs/CHECKS.md +1682 -0
  4. package/docs/COLLECTIONS.md +337 -0
  5. package/docs/CRAFT.md +277 -0
  6. package/docs/INTERVIEW.md +886 -0
  7. package/docs/LESSONS.md +172 -0
  8. package/docs/PROJECT.md +2657 -0
  9. package/docs/SOURCE.md +225 -0
  10. package/package.json +44 -4
  11. package/src/agent-bounce.ts +27 -0
  12. package/src/agent-guide.ts +237 -0
  13. package/src/amend.ts +193 -0
  14. package/src/applied-review.ts +104 -0
  15. package/src/ask-coverage.ts +69 -0
  16. package/src/assemble-answers.ts +53 -0
  17. package/src/cg-gates.ts +211 -0
  18. package/src/check-reads.ts +106 -0
  19. package/src/check.ts +1059 -0
  20. package/src/checkpoint-evidence-writer.ts +125 -0
  21. package/src/checkpoint-evidence.ts +152 -0
  22. package/src/choice-writers.ts +271 -0
  23. package/src/choices.ts +378 -0
  24. package/src/cli.ts +371 -0
  25. package/src/collection-answer.ts +136 -0
  26. package/src/collection-binding-reader.ts +496 -0
  27. package/src/collection-binding.ts +478 -0
  28. package/src/collection.ts +348 -0
  29. package/src/config-inputs.ts +155 -0
  30. package/src/config-lost.ts +135 -0
  31. package/src/correction-writers.ts +207 -0
  32. package/src/corrections.ts +685 -0
  33. package/src/diff-classes.ts +219 -0
  34. package/src/diff-map.ts +248 -0
  35. package/src/direction-citation.ts +13 -0
  36. package/src/direction-writers.ts +119 -0
  37. package/src/direction.ts +266 -0
  38. package/src/drafts.ts +363 -0
  39. package/src/feedback.ts +744 -0
  40. package/src/figure-mask.ts +143 -0
  41. package/src/fix-ledger.ts +355 -0
  42. package/src/init.ts +265 -0
  43. package/src/inputs.ts +160 -0
  44. package/src/intake.ts +158 -0
  45. package/src/interview.ts +53 -0
  46. package/src/invariants.ts +132 -0
  47. package/src/job-acts.ts +80 -0
  48. package/src/job-advance.ts +65 -0
  49. package/src/job-close.ts +226 -0
  50. package/src/job-host.ts +529 -0
  51. package/src/job-outbox.ts +383 -0
  52. package/src/job-publication.ts +701 -0
  53. package/src/job-runtime.ts +99 -0
  54. package/src/job-writer.ts +123 -0
  55. package/src/meshes-bust.ts +63 -0
  56. package/src/next.ts +1506 -0
  57. package/src/overrides.ts +42 -0
  58. package/src/package-upgrades.ts +32 -0
  59. package/src/packages-upgrade-check.ts +6 -0
  60. package/src/packages-upgrade.ts +168 -0
  61. package/src/parts-act.ts +96 -0
  62. package/src/parts-check.ts +242 -0
  63. package/src/parts-review.ts +196 -0
  64. package/src/parts-sections.ts +98 -0
  65. package/src/parts-verdict-record.ts +265 -0
  66. package/src/parts-verdict.ts +33 -0
  67. package/src/pause.ts +118 -0
  68. package/src/pins-check.ts +619 -0
  69. package/src/pins.ts +17 -0
  70. package/src/plan-bar-amendments.ts +54 -0
  71. package/src/plan-history.ts +147 -0
  72. package/src/plan-writers.ts +202 -0
  73. package/src/plate-receipt.ts +110 -0
  74. package/src/pre-interview.ts +302 -0
  75. package/src/prepared-inputs.ts +113 -0
  76. package/src/progress-writers.ts +181 -0
  77. package/src/provenance.ts +301 -0
  78. package/src/record-direction-target.ts +26 -0
  79. package/src/record-input.ts +18 -0
  80. package/src/refusal-baseline.ts +55 -0
  81. package/src/refusal.ts +30 -0
  82. package/src/reopen-lapses.ts +80 -0
  83. package/src/reopen.ts +144 -0
  84. package/src/republish.ts +93 -0
  85. package/src/restart.ts +141 -0
  86. package/src/review-act-runtime.ts +16 -0
  87. package/src/review-act.ts +592 -0
  88. package/src/review-package.ts +524 -0
  89. package/src/review-read.ts +238 -0
  90. package/src/review.ts +432 -0
  91. package/src/rig-version-date.ts +22 -0
  92. package/src/rig-version.ts +1047 -0
  93. package/src/run-record.ts +232 -0
  94. package/src/scene-authority.ts +410 -0
  95. package/src/scene-writers.ts +144 -0
  96. package/src/scene.ts +284 -0
  97. package/src/setup-painting.ts +46 -0
  98. package/src/signable.ts +65 -0
  99. package/src/signoff-answers.ts +76 -0
  100. package/src/signoff-checkpoint-act.ts +95 -0
  101. package/src/signoff-target.ts +127 -0
  102. package/src/signoff.ts +331 -0
  103. package/src/split-captions.ts +154 -0
  104. package/src/stage-progress.ts +379 -0
  105. package/src/stages.ts +148 -0
  106. package/src/step-hash.ts +19 -0
  107. package/src/structure-snapshot.ts +109 -0
  108. package/src/structure.ts +166 -0
  109. package/src/templates.ts +16 -0
  110. package/src/waypoints.ts +163 -0
  111. package/templates/agent-guide.md +108 -0
  112. package/templates/collection-agent-guide.md +72 -0
  113. package/templates/collection.json +252 -0
  114. package/templates/example-chain/AGENTS.md +134 -0
  115. package/templates/example-chain/CLAUDE.md +3 -0
  116. package/templates/example-chain/README.md +13 -0
  117. package/templates/example-chain/chain.json +2869 -0
  118. package/templates/example-chain/checkpoints/0a-interview-options/AGENTS.md +134 -0
  119. package/templates/example-chain/checkpoints/0a-interview-options/CLAUDE.md +3 -0
  120. package/templates/example-chain/checkpoints/0a-interview-options/evidence/.gitkeep +0 -0
  121. package/templates/example-chain/checkpoints/0a-interview-options/evidence/approval.txt +1 -0
  122. package/templates/example-chain/checkpoints/0a-interview-options/evidence/interview/rig_type.json +59 -0
  123. package/templates/example-chain/checkpoints/0a-interview-options/evidence/split-config.json +1 -0
  124. package/templates/example-chain/checkpoints/0a-interview-options/inputs/painting.png +0 -0
  125. package/templates/example-chain/checkpoints/0a-interview-options/inputs/references/.gitkeep +0 -0
  126. package/templates/example-chain/checkpoints/0a-interview-options/package.json +9 -0
  127. package/templates/example-chain/checkpoints/0a-interview-options/project.json +295 -0
  128. package/templates/example-chain/checkpoints/0a-interview-options.json +127 -0
  129. package/templates/example-chain/checkpoints/0b-plan-pending/AGENTS.md +134 -0
  130. package/templates/example-chain/checkpoints/0b-plan-pending/CLAUDE.md +3 -0
  131. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/.gitkeep +0 -0
  132. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/approval.txt +1 -0
  133. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/assemble.json +309 -0
  134. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/reference.json +59 -0
  135. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/rig_type.json +59 -0
  136. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/seethrough.json +186 -0
  137. package/templates/example-chain/checkpoints/0b-plan-pending/evidence/split-config.json +1 -0
  138. package/templates/example-chain/checkpoints/0b-plan-pending/inputs/painting.png +0 -0
  139. package/templates/example-chain/checkpoints/0b-plan-pending/inputs/references/.gitkeep +0 -0
  140. package/templates/example-chain/checkpoints/0b-plan-pending/package.json +9 -0
  141. package/templates/example-chain/checkpoints/0b-plan-pending/project.json +685 -0
  142. package/templates/example-chain/checkpoints/0b-plan-pending.json +81 -0
  143. package/templates/example-chain/checkpoints/1-parts-verdict/AGENTS.md +134 -0
  144. package/templates/example-chain/checkpoints/1-parts-verdict/CLAUDE.md +3 -0
  145. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/.gitkeep +0 -0
  146. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/approval.txt +1 -0
  147. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/assemble.json +309 -0
  148. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/reference.json +59 -0
  149. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/rig_type.json +59 -0
  150. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/seethrough.json +186 -0
  151. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/parts-check.json +127 -0
  152. package/templates/example-chain/checkpoints/1-parts-verdict/evidence/split-config.json +1 -0
  153. package/templates/example-chain/checkpoints/1-parts-verdict/inputs/painting.png +0 -0
  154. package/templates/example-chain/checkpoints/1-parts-verdict/inputs/references/.gitkeep +0 -0
  155. package/templates/example-chain/checkpoints/1-parts-verdict/package.json +9 -0
  156. package/templates/example-chain/checkpoints/1-parts-verdict/project.json +955 -0
  157. package/templates/example-chain/checkpoints/1-parts-verdict.json +204 -0
  158. package/templates/example-chain/checkpoints/2-rigging-answers/AGENTS.md +134 -0
  159. package/templates/example-chain/checkpoints/2-rigging-answers/CLAUDE.md +3 -0
  160. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/.gitkeep +0 -0
  161. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/approval.txt +1 -0
  162. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/diff-components.json +34 -0
  163. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/assemble.json +309 -0
  164. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/bones.json +190 -0
  165. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/reference.json +59 -0
  166. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/rig_type.json +59 -0
  167. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/seethrough.json +186 -0
  168. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/parts-check.json +127 -0
  169. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/parts-review.json +88 -0
  170. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
  171. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/split-config.json +1 -0
  172. package/templates/example-chain/checkpoints/2-rigging-answers/evidence/structure.json +30 -0
  173. package/templates/example-chain/checkpoints/2-rigging-answers/inputs/painting.png +0 -0
  174. package/templates/example-chain/checkpoints/2-rigging-answers/inputs/references/.gitkeep +0 -0
  175. package/templates/example-chain/checkpoints/2-rigging-answers/package.json +9 -0
  176. package/templates/example-chain/checkpoints/2-rigging-answers/project.json +1198 -0
  177. package/templates/example-chain/checkpoints/2-rigging-answers.json +254 -0
  178. package/templates/example-chain/checkpoints/3-rig-review/AGENTS.md +134 -0
  179. package/templates/example-chain/checkpoints/3-rig-review/CLAUDE.md +3 -0
  180. package/templates/example-chain/checkpoints/3-rig-review/evidence/.gitkeep +0 -0
  181. package/templates/example-chain/checkpoints/3-rig-review/evidence/approval.txt +1 -0
  182. package/templates/example-chain/checkpoints/3-rig-review/evidence/diff-components.json +34 -0
  183. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/assemble.json +309 -0
  184. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/bones.json +190 -0
  185. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/constraints.json +272 -0
  186. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/meshes.json +190 -0
  187. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/motion.json +272 -0
  188. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/reference.json +59 -0
  189. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/rig_type.json +59 -0
  190. package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/seethrough.json +186 -0
  191. package/templates/example-chain/checkpoints/3-rig-review/evidence/parts-check.json +127 -0
  192. package/templates/example-chain/checkpoints/3-rig-review/evidence/parts-review.json +88 -0
  193. package/templates/example-chain/checkpoints/3-rig-review/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
  194. package/templates/example-chain/checkpoints/3-rig-review/evidence/setup.png +0 -0
  195. package/templates/example-chain/checkpoints/3-rig-review/evidence/split-config.json +1 -0
  196. package/templates/example-chain/checkpoints/3-rig-review/evidence/structure.json +30 -0
  197. package/templates/example-chain/checkpoints/3-rig-review/evidence/v1/config.json +88 -0
  198. package/templates/example-chain/checkpoints/3-rig-review/evidence/v1/interview-requirements.json +8 -0
  199. package/templates/example-chain/checkpoints/3-rig-review/inputs/painting.png +0 -0
  200. package/templates/example-chain/checkpoints/3-rig-review/inputs/references/.gitkeep +0 -0
  201. package/templates/example-chain/checkpoints/3-rig-review/package.json +9 -0
  202. package/templates/example-chain/checkpoints/3-rig-review/project.json +1909 -0
  203. package/templates/example-chain/checkpoints/3-rig-review.json +424 -0
  204. package/templates/example-chain/checkpoints/4-direction-post/AGENTS.md +134 -0
  205. package/templates/example-chain/checkpoints/4-direction-post/CLAUDE.md +3 -0
  206. package/templates/example-chain/checkpoints/4-direction-post/evidence/.gitkeep +0 -0
  207. package/templates/example-chain/checkpoints/4-direction-post/evidence/approval.txt +1 -0
  208. package/templates/example-chain/checkpoints/4-direction-post/evidence/diff-components.json +34 -0
  209. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/assemble.json +309 -0
  210. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/bones.json +190 -0
  211. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/camera_depth.json +102 -0
  212. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/constraints.json +272 -0
  213. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/meshes.json +190 -0
  214. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/motion.json +272 -0
  215. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/post_composition.json +122 -0
  216. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/post_timing.json +102 -0
  217. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/reference.json +59 -0
  218. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/rig_type.json +59 -0
  219. package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/seethrough.json +186 -0
  220. package/templates/example-chain/checkpoints/4-direction-post/evidence/parts-check.json +127 -0
  221. package/templates/example-chain/checkpoints/4-direction-post/evidence/parts-review.json +88 -0
  222. package/templates/example-chain/checkpoints/4-direction-post/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
  223. package/templates/example-chain/checkpoints/4-direction-post/evidence/setup.png +0 -0
  224. package/templates/example-chain/checkpoints/4-direction-post/evidence/split-config.json +1 -0
  225. package/templates/example-chain/checkpoints/4-direction-post/evidence/structure.json +30 -0
  226. package/templates/example-chain/checkpoints/4-direction-post/evidence/v1/config.json +88 -0
  227. package/templates/example-chain/checkpoints/4-direction-post/evidence/v1/interview-requirements.json +8 -0
  228. package/templates/example-chain/checkpoints/4-direction-post/inputs/painting.png +0 -0
  229. package/templates/example-chain/checkpoints/4-direction-post/inputs/references/.gitkeep +0 -0
  230. package/templates/example-chain/checkpoints/4-direction-post/package.json +9 -0
  231. package/templates/example-chain/checkpoints/4-direction-post/project.json +2302 -0
  232. package/templates/example-chain/checkpoints/4-direction-post.json +456 -0
  233. package/templates/example-chain/evidence/.gitkeep +0 -0
  234. package/templates/example-chain/evidence/approval.txt +1 -0
  235. package/templates/example-chain/evidence/captions.json +101 -0
  236. package/templates/example-chain/evidence/diff-components.json +34 -0
  237. package/templates/example-chain/evidence/direction/v2.json +74 -0
  238. package/templates/example-chain/evidence/direction-input.json +74 -0
  239. package/templates/example-chain/evidence/interview/assemble.json +309 -0
  240. package/templates/example-chain/evidence/interview/bones.json +190 -0
  241. package/templates/example-chain/evidence/interview/camera_depth.json +102 -0
  242. package/templates/example-chain/evidence/interview/constraints.json +272 -0
  243. package/templates/example-chain/evidence/interview/meshes.json +190 -0
  244. package/templates/example-chain/evidence/interview/motion.json +272 -0
  245. package/templates/example-chain/evidence/interview/post_composition.json +122 -0
  246. package/templates/example-chain/evidence/interview/post_timing.json +102 -0
  247. package/templates/example-chain/evidence/interview/reference.json +59 -0
  248. package/templates/example-chain/evidence/interview/rig_type.json +59 -0
  249. package/templates/example-chain/evidence/interview/seethrough.json +186 -0
  250. package/templates/example-chain/evidence/parts-check.json +127 -0
  251. package/templates/example-chain/evidence/parts-review.json +88 -0
  252. package/templates/example-chain/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
  253. package/templates/example-chain/evidence/scene/s1.json +65 -0
  254. package/templates/example-chain/evidence/scene-citations.json +9 -0
  255. package/templates/example-chain/evidence/setup.png +0 -0
  256. package/templates/example-chain/evidence/split-config.json +1 -0
  257. package/templates/example-chain/evidence/structure.json +30 -0
  258. package/templates/example-chain/evidence/v1/config.json +88 -0
  259. package/templates/example-chain/evidence/v1/interview-requirements.json +8 -0
  260. package/templates/example-chain/inputs/background.png +0 -0
  261. package/templates/example-chain/inputs/painting.png +0 -0
  262. package/templates/example-chain/inputs/references/.gitkeep +0 -0
  263. package/templates/example-chain/package.json +9 -0
  264. package/templates/example-chain/project.json +2593 -0
  265. package/templates/example-diff/diff-components.json +71 -0
  266. package/templates/example-diff/recomposite_rig.png +0 -0
  267. package/templates/example-direction/approval.txt +1 -0
  268. package/templates/example-direction/derived/review/v1/out/check/build/rectangle.png +0 -0
  269. package/templates/example-direction/derived/review/v1/out/check/build/run.json +40 -0
  270. package/templates/example-direction/derived/review/v1/out/check/build/skeleton.atlas +8 -0
  271. package/templates/example-direction/derived/review/v1/out/check/build/skeleton.json +73 -0
  272. package/templates/example-direction/derived/review/v1/out/check/build/skeleton.model.json +195 -0
  273. package/templates/example-direction/evidence/direction/v2.json +69 -0
  274. package/templates/example-direction/evidence/direction/v3.json +59 -0
  275. package/templates/example-direction/evidence/images/rectangle.png +0 -0
  276. package/templates/example-direction/evidence/input.json +69 -0
  277. package/templates/example-direction/evidence/motion.json +33 -0
  278. package/templates/example-direction/evidence/patch.json +23 -0
  279. package/templates/example-direction/evidence/rig-config.json +55 -0
  280. package/templates/example-direction/evidence/still.png +0 -0
  281. package/templates/example-direction/project.json +372 -0
  282. package/templates/example-direction/reasoning.txt +1 -0
  283. package/templates/example-direction/words.txt +1 -0
  284. package/templates/example-draft/README.md +28 -0
  285. package/templates/example-draft/config.json +71 -0
  286. package/templates/example-draft/derived/drafts/lantern/config.json +71 -0
  287. package/templates/example-draft/derived/drafts/lantern/input/painting_in_rig.png +0 -0
  288. package/templates/example-draft/derived/drafts/lantern/input/parts/background.png +0 -0
  289. package/templates/example-draft/derived/drafts/lantern/input/parts/eye_l.png +0 -0
  290. package/templates/example-draft/derived/drafts/lantern/input/parts/eye_r.png +0 -0
  291. package/templates/example-draft/derived/drafts/lantern/input/parts/face.png +0 -0
  292. package/templates/example-draft/derived/drafts/lantern/input/parts/lantern.png +0 -0
  293. package/templates/example-draft/derived/drafts/lantern/input/parts/torso.png +0 -0
  294. package/templates/example-draft/derived/drafts/lantern/input/parts.json +188 -0
  295. package/templates/example-draft/derived/drafts/lantern/input/recomposite_rig.png +0 -0
  296. package/templates/example-draft/derived/drafts/lantern/interview-requirements.json +8 -0
  297. package/templates/example-draft/derived/drafts/lantern/out/check/build/run.json +40 -0
  298. package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.atlas +28 -0
  299. package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.json +193 -0
  300. package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.model.json +483 -0
  301. package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.png +0 -0
  302. package/templates/example-draft/derived/drafts/lantern/out/images/background.png +0 -0
  303. package/templates/example-draft/derived/drafts/lantern/out/images/eye_l.png +0 -0
  304. package/templates/example-draft/derived/drafts/lantern/out/images/eye_r.png +0 -0
  305. package/templates/example-draft/derived/drafts/lantern/out/images/face.png +0 -0
  306. package/templates/example-draft/derived/drafts/lantern/out/images/lantern.png +0 -0
  307. package/templates/example-draft/derived/drafts/lantern/out/images/torso.png +0 -0
  308. package/templates/example-draft/derived/drafts/lantern/out/mesh_report.json +1 -0
  309. package/templates/example-draft/derived/drafts/lantern/out/motion.json +137 -0
  310. package/templates/example-draft/derived/drafts/lantern/out/rig.json +102 -0
  311. package/templates/example-draft/derived/drafts/lantern/out/run.json +52 -0
  312. package/templates/example-draft/derived/parts/rig/painting_in_rig.png +0 -0
  313. package/templates/example-draft/derived/parts/rig/parts/background.png +0 -0
  314. package/templates/example-draft/derived/parts/rig/parts/eye_l.png +0 -0
  315. package/templates/example-draft/derived/parts/rig/parts/eye_r.png +0 -0
  316. package/templates/example-draft/derived/parts/rig/parts/face.png +0 -0
  317. package/templates/example-draft/derived/parts/rig/parts/lantern.png +0 -0
  318. package/templates/example-draft/derived/parts/rig/parts/torso.png +0 -0
  319. package/templates/example-draft/derived/parts/rig/parts.json +188 -0
  320. package/templates/example-draft/derived/parts/rig/recomposite_rig.png +0 -0
  321. package/templates/example-draft/derived/review/v1/config.json +71 -0
  322. package/templates/example-draft/derived/review/v1/interview-requirements.json +8 -0
  323. package/templates/example-draft/derived/review/v1/out/check/build/run.json +40 -0
  324. package/templates/example-draft/derived/review/v1/out/check/build/skeleton.atlas +28 -0
  325. package/templates/example-draft/derived/review/v1/out/check/build/skeleton.json +193 -0
  326. package/templates/example-draft/derived/review/v1/out/check/build/skeleton.model.json +483 -0
  327. package/templates/example-draft/derived/review/v1/out/check/build/skeleton.png +0 -0
  328. package/templates/example-draft/derived/review/v1/out/parts/background.png +0 -0
  329. package/templates/example-draft/derived/review/v1/out/parts/eye_l.png +0 -0
  330. package/templates/example-draft/derived/review/v1/out/parts/eye_r.png +0 -0
  331. package/templates/example-draft/derived/review/v1/out/parts/face.png +0 -0
  332. package/templates/example-draft/derived/review/v1/out/parts/lantern.png +0 -0
  333. package/templates/example-draft/derived/review/v1/out/parts/torso.png +0 -0
  334. package/templates/example-draft/derived/review/v1/out/parts.json +188 -0
  335. package/templates/example-draft/derived/review/v1/out/rig/images/background.png +0 -0
  336. package/templates/example-draft/derived/review/v1/out/rig/images/eye_l.png +0 -0
  337. package/templates/example-draft/derived/review/v1/out/rig/images/eye_r.png +0 -0
  338. package/templates/example-draft/derived/review/v1/out/rig/images/face.png +0 -0
  339. package/templates/example-draft/derived/review/v1/out/rig/images/lantern.png +0 -0
  340. package/templates/example-draft/derived/review/v1/out/rig/images/torso.png +0 -0
  341. package/templates/example-draft/derived/review/v1/out/rig/mesh_report.json +1 -0
  342. package/templates/example-draft/derived/review/v1/out/rig/motion.json +137 -0
  343. package/templates/example-draft/derived/review/v1/out/rig/rig.json +102 -0
  344. package/templates/example-draft/derived/review/v1/out/rig/run.json +52 -0
  345. package/templates/example-draft/derived/review/v2/config.json +71 -0
  346. package/templates/example-draft/derived/review/v2/interview-requirements.json +8 -0
  347. package/templates/example-draft/derived/review/v2/out/check/build/run.json +40 -0
  348. package/templates/example-draft/derived/review/v2/out/check/build/skeleton.atlas +28 -0
  349. package/templates/example-draft/derived/review/v2/out/check/build/skeleton.json +193 -0
  350. package/templates/example-draft/derived/review/v2/out/check/build/skeleton.model.json +483 -0
  351. package/templates/example-draft/derived/review/v2/out/check/build/skeleton.png +0 -0
  352. package/templates/example-draft/derived/review/v2/out/parts/background.png +0 -0
  353. package/templates/example-draft/derived/review/v2/out/parts/eye_l.png +0 -0
  354. package/templates/example-draft/derived/review/v2/out/parts/eye_r.png +0 -0
  355. package/templates/example-draft/derived/review/v2/out/parts/face.png +0 -0
  356. package/templates/example-draft/derived/review/v2/out/parts/lantern.png +0 -0
  357. package/templates/example-draft/derived/review/v2/out/parts/torso.png +0 -0
  358. package/templates/example-draft/derived/review/v2/out/parts.json +188 -0
  359. package/templates/example-draft/derived/review/v2/out/rig/images/background.png +0 -0
  360. package/templates/example-draft/derived/review/v2/out/rig/images/eye_l.png +0 -0
  361. package/templates/example-draft/derived/review/v2/out/rig/images/eye_r.png +0 -0
  362. package/templates/example-draft/derived/review/v2/out/rig/images/face.png +0 -0
  363. package/templates/example-draft/derived/review/v2/out/rig/images/lantern.png +0 -0
  364. package/templates/example-draft/derived/review/v2/out/rig/images/torso.png +0 -0
  365. package/templates/example-draft/derived/review/v2/out/rig/mesh_report.json +1 -0
  366. package/templates/example-draft/derived/review/v2/out/rig/motion.json +137 -0
  367. package/templates/example-draft/derived/review/v2/out/rig/rig.json +102 -0
  368. package/templates/example-draft/derived/review/v2/out/rig/run.json +52 -0
  369. package/templates/example-draft/evidence/diff-components.json +71 -0
  370. package/templates/example-draft/evidence/direction/v4.json +64 -0
  371. package/templates/example-draft/evidence/direction-input-v4.json +64 -0
  372. package/templates/example-draft/evidence/fix-ledger/lantern.json +17 -0
  373. package/templates/example-draft/evidence/parts-check.json +127 -0
  374. package/templates/example-draft/evidence/parts-review.json +88 -0
  375. package/templates/example-draft/evidence/review-reads/4ed0507381809746443021554e012032be5b5f8417da0f2e525e9b6df16e9f05.json +59 -0
  376. package/templates/example-draft/evidence/scene/s1.json +65 -0
  377. package/templates/example-draft/evidence/scene-background.png +0 -0
  378. package/templates/example-draft/evidence/scene-citations.json +9 -0
  379. package/templates/example-draft/evidence/structure-decisions.json +9 -0
  380. package/templates/example-draft/evidence/v1/interview-config.json +71 -0
  381. package/templates/example-draft/evidence/v1/interview-requirements.json +8 -0
  382. package/templates/example-draft/evidence/v1/review-config.json +71 -0
  383. package/templates/example-draft/evidence/v2/interview-config.json +71 -0
  384. package/templates/example-draft/evidence/v2/interview-requirements.json +8 -0
  385. package/templates/example-draft/evidence/v2/review-config.json +71 -0
  386. package/templates/example-draft/input/painting_in_rig.png +0 -0
  387. package/templates/example-draft/input/parts/background.png +0 -0
  388. package/templates/example-draft/input/parts/eye_l.png +0 -0
  389. package/templates/example-draft/input/parts/eye_r.png +0 -0
  390. package/templates/example-draft/input/parts/face.png +0 -0
  391. package/templates/example-draft/input/parts/lantern.png +0 -0
  392. package/templates/example-draft/input/parts/torso.png +0 -0
  393. package/templates/example-draft/input/parts.json +188 -0
  394. package/templates/example-draft/input/recomposite_rig.png +0 -0
  395. package/templates/example-draft/inputs/painting.png +0 -0
  396. package/templates/example-draft/interview-requirements.json +8 -0
  397. package/templates/example-draft/out/check/build/run.json +40 -0
  398. package/templates/example-draft/out/check/build/skeleton.atlas +28 -0
  399. package/templates/example-draft/out/check/build/skeleton.json +193 -0
  400. package/templates/example-draft/out/check/build/skeleton.model.json +483 -0
  401. package/templates/example-draft/out/check/build/skeleton.png +0 -0
  402. package/templates/example-draft/out/images/background.png +0 -0
  403. package/templates/example-draft/out/images/eye_l.png +0 -0
  404. package/templates/example-draft/out/images/eye_r.png +0 -0
  405. package/templates/example-draft/out/images/face.png +0 -0
  406. package/templates/example-draft/out/images/lantern.png +0 -0
  407. package/templates/example-draft/out/images/torso.png +0 -0
  408. package/templates/example-draft/out/mesh_report.json +1 -0
  409. package/templates/example-draft/out/motion.json +137 -0
  410. package/templates/example-draft/out/rig.json +102 -0
  411. package/templates/example-draft/out/run.json +52 -0
  412. package/templates/example-draft/outputs/v1/out/check/build/run.json +40 -0
  413. package/templates/example-draft/outputs/v1/out/check/build/skeleton.atlas +28 -0
  414. package/templates/example-draft/outputs/v1/out/check/build/skeleton.json +193 -0
  415. package/templates/example-draft/outputs/v1/out/check/build/skeleton.model.json +483 -0
  416. package/templates/example-draft/outputs/v1/out/check/build/skeleton.png +0 -0
  417. package/templates/example-draft/outputs/v1/out/painting_in_rig.png +0 -0
  418. package/templates/example-draft/outputs/v1/out/parts/background.png +0 -0
  419. package/templates/example-draft/outputs/v1/out/parts/eye_l.png +0 -0
  420. package/templates/example-draft/outputs/v1/out/parts/eye_r.png +0 -0
  421. package/templates/example-draft/outputs/v1/out/parts/face.png +0 -0
  422. package/templates/example-draft/outputs/v1/out/parts/lantern.png +0 -0
  423. package/templates/example-draft/outputs/v1/out/parts/torso.png +0 -0
  424. package/templates/example-draft/outputs/v1/out/parts.json +188 -0
  425. package/templates/example-draft/outputs/v1/out/rig/images/background.png +0 -0
  426. package/templates/example-draft/outputs/v1/out/rig/images/eye_l.png +0 -0
  427. package/templates/example-draft/outputs/v1/out/rig/images/eye_r.png +0 -0
  428. package/templates/example-draft/outputs/v1/out/rig/images/face.png +0 -0
  429. package/templates/example-draft/outputs/v1/out/rig/images/lantern.png +0 -0
  430. package/templates/example-draft/outputs/v1/out/rig/images/torso.png +0 -0
  431. package/templates/example-draft/outputs/v1/out/rig/mesh_report.json +1 -0
  432. package/templates/example-draft/outputs/v1/out/rig/motion.json +137 -0
  433. package/templates/example-draft/outputs/v1/out/rig/rig.json +102 -0
  434. package/templates/example-draft/outputs/v1/out/rig/run.json +52 -0
  435. package/templates/example-draft/outputs/v2/out/check/build/run.json +40 -0
  436. package/templates/example-draft/outputs/v2/out/check/build/skeleton.atlas +28 -0
  437. package/templates/example-draft/outputs/v2/out/check/build/skeleton.json +193 -0
  438. package/templates/example-draft/outputs/v2/out/check/build/skeleton.model.json +483 -0
  439. package/templates/example-draft/outputs/v2/out/check/build/skeleton.png +0 -0
  440. package/templates/example-draft/outputs/v2/out/painting_in_rig.png +0 -0
  441. package/templates/example-draft/outputs/v2/out/parts/background.png +0 -0
  442. package/templates/example-draft/outputs/v2/out/parts/eye_l.png +0 -0
  443. package/templates/example-draft/outputs/v2/out/parts/eye_r.png +0 -0
  444. package/templates/example-draft/outputs/v2/out/parts/face.png +0 -0
  445. package/templates/example-draft/outputs/v2/out/parts/lantern.png +0 -0
  446. package/templates/example-draft/outputs/v2/out/parts/torso.png +0 -0
  447. package/templates/example-draft/outputs/v2/out/parts.json +188 -0
  448. package/templates/example-draft/outputs/v2/out/rig/images/background.png +0 -0
  449. package/templates/example-draft/outputs/v2/out/rig/images/eye_l.png +0 -0
  450. package/templates/example-draft/outputs/v2/out/rig/images/eye_r.png +0 -0
  451. package/templates/example-draft/outputs/v2/out/rig/images/face.png +0 -0
  452. package/templates/example-draft/outputs/v2/out/rig/images/lantern.png +0 -0
  453. package/templates/example-draft/outputs/v2/out/rig/images/torso.png +0 -0
  454. package/templates/example-draft/outputs/v2/out/rig/mesh_report.json +1 -0
  455. package/templates/example-draft/outputs/v2/out/rig/motion.json +137 -0
  456. package/templates/example-draft/outputs/v2/out/rig/rig.json +102 -0
  457. package/templates/example-draft/outputs/v2/out/rig/run.json +52 -0
  458. package/templates/example-interview-choice.json +1068 -0
  459. package/templates/example-interview-options/constraint.json +376 -0
  460. package/templates/example-interview-options/example-assemble.json +376 -0
  461. package/templates/example-interview-options/example-bones.json +190 -0
  462. package/templates/example-interview-options/example-camera-depth.json +99 -0
  463. package/templates/example-interview-options/example-constraints.json +272 -0
  464. package/templates/example-interview-options/example-meshes.json +190 -0
  465. package/templates/example-interview-options/example-motion.json +272 -0
  466. package/templates/example-interview-options/example-picks.json +15 -0
  467. package/templates/example-interview-options/example-post-composition.json +110 -0
  468. package/templates/example-interview-options/example-post-timing.json +105 -0
  469. package/templates/example-interview-options/example-reference.json +63 -0
  470. package/templates/example-interview-options/example-rig-type.json +63 -0
  471. package/templates/example-interview-options/example-seethrough.json +190 -0
  472. package/templates/example-interview-options/fullscreen.json +63 -0
  473. package/templates/example-interview-options/other-explicit.json +63 -0
  474. package/templates/example-interview-options/other-pending.json +63 -0
  475. package/templates/example-interview-options/other-picks.json +16 -0
  476. package/templates/example-interview.md +213 -0
  477. package/templates/example-invariants/invariants.json +1339 -0
  478. package/templates/example-prepared/prepared.svg +1 -0
  479. package/templates/example-prepared/source.svg +1 -0
  480. package/templates/example-project.json +4259 -0
  481. package/templates/example-remesh/AGENTS.md +134 -0
  482. package/templates/example-remesh/CLAUDE.md +3 -0
  483. package/templates/example-remesh/evidence/.gitkeep +0 -0
  484. package/templates/example-remesh/evidence/approval.txt +1 -0
  485. package/templates/example-remesh/evidence/captions.json +101 -0
  486. package/templates/example-remesh/evidence/diff-components.json +34 -0
  487. package/templates/example-remesh/evidence/direction/v4.json +74 -0
  488. package/templates/example-remesh/evidence/direction/v5.json +74 -0
  489. package/templates/example-remesh/evidence/direction-input-v4.json +74 -0
  490. package/templates/example-remesh/evidence/direction-input-v5.json +74 -0
  491. package/templates/example-remesh/evidence/interview/assemble.json +309 -0
  492. package/templates/example-remesh/evidence/interview/bones.json +190 -0
  493. package/templates/example-remesh/evidence/interview/camera_depth.json +103 -0
  494. package/templates/example-remesh/evidence/interview/constraints.json +272 -0
  495. package/templates/example-remesh/evidence/interview/meshes.json +190 -0
  496. package/templates/example-remesh/evidence/interview/motion.json +272 -0
  497. package/templates/example-remesh/evidence/interview/post_composition.json +123 -0
  498. package/templates/example-remesh/evidence/interview/post_timing.json +103 -0
  499. package/templates/example-remesh/evidence/interview/reference.json +59 -0
  500. package/templates/example-remesh/evidence/interview/rig_type.json +59 -0
  501. package/templates/example-remesh/evidence/interview/seethrough.json +186 -0
  502. package/templates/example-remesh/evidence/parts-check.json +127 -0
  503. package/templates/example-remesh/evidence/parts-review.json +88 -0
  504. package/templates/example-remesh/evidence/reopen.txt +1 -0
  505. package/templates/example-remesh/evidence/review-reads/6a17db694db96f1b6049a61a017e0c8e66757b982b8851e0187e561cfcf9af68.json +59 -0
  506. package/templates/example-remesh/evidence/scene/s1.json +69 -0
  507. package/templates/example-remesh/evidence/scene-citations.json +9 -0
  508. package/templates/example-remesh/evidence/setup.png +0 -0
  509. package/templates/example-remesh/evidence/split-config.json +1 -0
  510. package/templates/example-remesh/evidence/structure.json +30 -0
  511. package/templates/example-remesh/evidence/v1/registration/at-source.json +44 -0
  512. package/templates/example-remesh/evidence/v1/registration/citations.json +68 -0
  513. package/templates/example-remesh/evidence/v1/registration/compile-report.json +368 -0
  514. package/templates/example-remesh/evidence/v1/registration/config-source.json +88 -0
  515. package/templates/example-remesh/evidence/v1/registration/config.json +88 -0
  516. package/templates/example-remesh/evidence/v1/registration/interview-requirements.json +8 -0
  517. package/templates/example-remesh/evidence/v1/registration/package-lock.json +11 -0
  518. package/templates/example-remesh/evidence/v1/registration/path-adapter.json +1 -0
  519. package/templates/example-remesh/evidence/v1/registration/rigc-run-1.json +44 -0
  520. package/templates/example-remesh/evidence/v2/registration/at-source.json +44 -0
  521. package/templates/example-remesh/evidence/v2/registration/citations.json +68 -0
  522. package/templates/example-remesh/evidence/v2/registration/compile-report.json +368 -0
  523. package/templates/example-remesh/evidence/v2/registration/config-source.json +88 -0
  524. package/templates/example-remesh/evidence/v2/registration/config.json +88 -0
  525. package/templates/example-remesh/evidence/v2/registration/interview-requirements.json +8 -0
  526. package/templates/example-remesh/evidence/v2/registration/package-lock.json +11 -0
  527. package/templates/example-remesh/evidence/v2/registration/path-adapter.json +1 -0
  528. package/templates/example-remesh/evidence/v2/registration/rigc-run-1.json +44 -0
  529. package/templates/example-remesh/evidence/v3/registration/at-source.json +44 -0
  530. package/templates/example-remesh/evidence/v3/registration/citations.json +68 -0
  531. package/templates/example-remesh/evidence/v3/registration/compile-report.json +368 -0
  532. package/templates/example-remesh/evidence/v3/registration/config-source.json +88 -0
  533. package/templates/example-remesh/evidence/v3/registration/config.json +88 -0
  534. package/templates/example-remesh/evidence/v3/registration/interview-requirements.json +8 -0
  535. package/templates/example-remesh/evidence/v3/registration/package-lock.json +11 -0
  536. package/templates/example-remesh/evidence/v3/registration/path-adapter.json +1 -0
  537. package/templates/example-remesh/evidence/v3/registration/rigc-run-1.json +44 -0
  538. package/templates/example-remesh/inputs/background.png +0 -0
  539. package/templates/example-remesh/inputs/painting.png +0 -0
  540. package/templates/example-remesh/inputs/references/.gitkeep +0 -0
  541. package/templates/example-remesh/package.json +9 -0
  542. package/templates/example-remesh/project.json +3916 -0
  543. package/templates/example-reopened/project.json +4568 -0
  544. package/templates/example-review/config.json +1 -0
  545. package/templates/example-review/out/check/build/skeleton.model.json +1 -0
  546. package/templates/example-review/out/parts/piece.svg +1 -0
  547. package/templates/example-review/out/parts.json +1 -0
  548. package/templates/example-review/out/rig/mesh_report.json +1 -0
  549. package/templates/example-review/out/rig/motion.json +1 -0
  550. package/templates/example-review/out/rig/rig.json +1 -0
  551. package/templates/example-split/captions.json +357 -0
  552. package/templates/example-split/config.json +215 -0
  553. package/templates/legacy-example-project.json +1071 -0
  554. package/templates/project.json +199 -0
  555. package/templates/shared/conventions.json +23 -0
@@ -0,0 +1,2657 @@
1
+ # A project
2
+
3
+ One painting in, one rig (and the takes made from it) out. Each rig is a
4
+ **project**: a folder of its own that holds the painting, the user's direction,
5
+ the plan the user signed, every version shown and every verdict given. Without
6
+ it, the story of a rig is split across a chat thread, a scratch folder and
7
+ someone's memory, and every resume re-reads context and re-guesses.
8
+
9
+ A project's `project.json` is **the only place a decision is recorded**. Chat
10
+ is not a record: a decision made in chat is copied here, verbatim, with who said
11
+ it and when.
12
+
13
+ The field set is **v0** — `"schema": "rig-create/project@0"` — and is expected
14
+ to change while the structure is piloted by hand. The `@0` is there so a reader
15
+ of a record can tell which shape it follows once the shape moves; a validator
16
+ and a migration come with the first shape change, not before. The empty
17
+ skeleton is [../templates/project.json](../templates/project.json); a filled
18
+ record is [../templates/example-project.json](../templates/example-project.json).
19
+ Its sign-offs are fixture sign-offs, not a person's approval; see
20
+ [../templates/example-draft/README.md](../templates/example-draft/README.md).
21
+
22
+ ## Layout
23
+
24
+ ```
25
+ <project>/
26
+ project.json the decision record: interview, plan, split, stage, sign-offs, versions
27
+ AGENTS.md the agent guide: the order, and which installed docs to read; CLAUDE.md imports it
28
+ inputs/ immutable: the painting, the user's references
29
+ references/
30
+ evidence/v<N>/ what a verdict rested on: crops, sheets, check tables
31
+ derived/ regenerable intermediates (ignored by git)
32
+ outputs/v<N>/ skeleton, atlas, takes (ignored by git)
33
+ scratch/ anything; deletable any time (ignored by git)
34
+ ```
35
+
36
+ A project is its own git repository, so the history of its decisions is its
37
+ `git log`. A collection member is the exception: it lives in its collection's
38
+ repository, which holds its history ([COLLECTIONS.md § Making one](COLLECTIONS.md#making-one)). `rig-create init <dir>` writes the layout above, a `.gitignore`
39
+ for the untracked folders, a `package.json` pinning the rig-c packages, and a
40
+ first commit.
41
+
42
+ ## Data is split by lifetime
43
+
44
+ Rigging derives far more data than it ships. Sorting every file by how long it
45
+ must live keeps the record small and makes "can I delete this?" a question the
46
+ folder answers.
47
+
48
+ | Folder | Lifetime | Tracked | Rule |
49
+ | --- | --- | --- | --- |
50
+ | `project.json` | forever | yes | The only place a decision is recorded. |
51
+ | `inputs/` | forever, never edited | yes | Every file has its sha256 in `project.json` `inputs[]`. A changed input is a new file with a new name, not an edit — otherwise a verdict given on the old file cannot be re-read. |
52
+ | `evidence/v<N>/` | forever | yes | The crop, sheet or table a bounce or an acceptance rested on, for the version it judged. If it is not here, the verdict cannot be re-read later. Keep each file small; a larger review image stays where it is and is listed in `versions[].evidence` with its path and sha256. |
53
+ | `derived/` | until regenerated | no | Depth maps, masks, layers, configs, frames. Every file (or folder) is listed in `derived[]` with the command and the input hashes that made it. Something that cannot be listed that way is either an input or scratch. |
54
+ | `outputs/v<N>/` | until discarded | no | One folder per version that was shown to anyone, frozen when shown. Hashes go into `versions[].outputs`. If the user keeps finals elsewhere, `archive` records where. |
55
+ | `scratch/` | none | no | Anything. Deleting it must never lose information; if it would, the file belongs in `evidence/` or `derived/`. |
56
+
57
+ ## Stages
58
+
59
+ `interview` → `plan-approved` → `parts` → `parts-review` → `rigging-interview` → `rigging` →
60
+ `rig-review` → `post-interview` → `post` → `direction-review` → `accepted`,
61
+ plus `paused`.
62
+
63
+ **A stage moves only on a human `signoffs[]` entry**: the stage it closes, who,
64
+ when, and the words used. A plan checkpoint is signed the same way, with
65
+ `checkpoint` naming the plan step. An agent may draft, propose, build and
66
+ measure in any stage; it may not move `stage` forward or pass a checkpoint by
67
+ itself. Reason: the stages exist to put a person's eyes before each piece of
68
+ work that is expensive to redo; an agent that could advance them would be
69
+ grading its own work.
70
+
71
+ A project is *in* a stage while that stage's work happens; the sign-off named
72
+ in the last column ends it and moves the project to the next. The order and
73
+ which stages end on a sign-off are written once, in `src/stages.ts`;
74
+ `rig-create interview --check` refuses a stage moved past a missing sign-off
75
+ ([CHECKS.md](CHECKS.md)).
76
+
77
+ | Stage | User step | What happens | Ends with |
78
+ | --- | --- | --- | --- |
79
+ | `interview` | 1 | Framing and source/parts guides (the later rig guides wait for rigging-interview) ([INTERVIEW.md](INTERVIEW.md)): the user's sentences, a delegation ("auto"), or not applicable — never left for the agent to fill. | The user signs `{ "stage": "interview" }`. |
80
+ | `plan-approved` | 1 | The parts steps (`phase: "rig"`) are drafted from the guides, with the agent's decision written on every delegated item. The user sets each step's checkpoint and signs. | `{ "stage": "plan-approved" }`. |
81
+ | `parts` | 1 | The rig steps that make the parts run: `seethrough` and `assemble`. At a step whose checkpoint is on, work stops until `{ "stage": "parts", "checkpoint": "<step id>" }` is signed (the split, [below](#split-approval-is-a-plan-checkpoint)). Only a labelled draft may contain proposed bones/weights for mesh/chest inspection; production rigging remains forbidden. | Every step it runs is `done`, `assemble`'s bar including the parts check passing on the assembled parts. |
82
+ | `parts-review` | 1 | **The parts are reviewed before any production rig is built** ([below](#the-parts-are-reviewed-before-rigging)): the user looks at the assembled parts in rig-review's parts view. A bounce is fixed here, in `parts`. | `{ "stage": "parts-review" }`, signed by a person, carrying the approved parts verdict as `evidence`. |
83
+ | `rigging-interview` | 2 | Ask bones, meshes, constraints and motion while viewing the approved parts and structure; draft their rigging plan. | The human signs `rigging-interview`, binding accepted parts and structure. |
84
+ | `rigging` | 3 | The remaining rig steps run in order (`bones`, `meshes`, `constraints`, `motion`); each config section cites its guide (`versions[].provenance`). At a step whose checkpoint is on, work stops until `{ "stage": "rigging", "checkpoint": "<step id>" }` is signed. It does not start without the `parts-review` sign-off (`PARTS_REVIEW_UNSIGNED`), nor on a verdict that is missing, not approved or no longer the parts on disk (`PARTS_VERDICT_*`). | The last rig step's bar is met. |
85
+ | `rig-review` | 1 → 3 | **First review, on a still frame**: the rig alone, fixed camera, no post-production — seams, cuts, physics and motion played in place. Feedback is recorded verbatim with `on_items`; a bounce makes a new rig version. | The user accepts one rig version: an `accepted` verdict with `review: "rig-review"` on it, resting on its still frame, and `{ "stage": "rig-review" }` signed ([below](#the-rig-review-rests-on-a-still-frame)). |
86
+ | `post-interview` | 2 | The post items (camera and 2.5D, timing, composition) are asked, and the post steps (`phase: "post"`) drafted from them. | The user signs `{ "stage": "post-interview" }` — guides and post steps together. |
87
+ | `post` | 2 | Post-production on the accepted rig version (`versions[].kind: "post"`, `rig_version` naming it); post tooling asks `rig-create handoff` first ([below](#the-hand-off-to-post-production)). | The last post step's bar is met. |
88
+ | `direction-review` | 2 → 3 | **Second review, direction**: camera, depth, timing, framing. Same feedback rule. | The user accepts one post version. |
89
+ | `accepted` | — | Done. If finals go somewhere else, `archive` says where. | — |
90
+ | `paused` | — | Work stopped by a human sign-off, entered and left through `pause` and `resume` ([below](#pausing-and-resuming)). While paused it is checked as the first stage whose sign-off is missing; `resume` returns it to the stage it was paused from. | — |
91
+
92
+ **Why two reviews.** The rig is reviewed first on a still frame with a fixed
93
+ camera — the rig alone — so a seam cannot hide behind a camera move, and a
94
+ depth complaint cannot bounce a correct rig. `post-interview` mirrors
95
+ `interview`: post guides are asked only once the rig they apply to is accepted,
96
+ so they are not asked about a rig that may still change. A rig change after
97
+ `rig-review` means a new rig version and a new first review, and the post
98
+ version built on the old rig is redone on the new one.
99
+
100
+ ### Refusals a record already carries
101
+
102
+ A writer validates its whole candidate with `checkProject` and refuses only a
103
+ refusal the record as read did not already carry. The baseline is that check at
104
+ write time and is never stored. Findings match by code and path with
105
+ multiplicity, so a carried code at a new path is new, and the refusal names only
106
+ the new code. `reopen`, `signoff`, `step-status`, `pause`, `resume`, `amend` and
107
+ `draft-rig` and `review` follow this rule; the interview, plan, scene, direction, correction,
108
+ `rig-version`, `version config-lost` and `version outputs-lost` writers already did. Each writer's own
109
+ pre-checks are unchanged (`signoff` still refuses a carried lag, and a carried
110
+ `GUIDE_CHANGED_UNSIGNED` on a step it covers), and `check` still reports every
111
+ carried refusal.
112
+ `advance` still needs 0 refusals: it is the stage boundary. A check that stops
113
+ early (an unreadable record, `SCHEMA_UNKNOWN`, `STAGE_UNKNOWN`) read nothing
114
+ after that point, so it gives no baseline to any writer above: each one refuses
115
+ on such a record, and a pending answer's stored `refusal_baseline` does not
116
+ stand in for it.
117
+
118
+ ### Recording forward progress
119
+
120
+ ```sh
121
+ rig-create advance <stage> [<project dir>] [--json]
122
+ rig-create advance parts-review --parts-check evidence/parts-check.json --parts <assembled dir>
123
+ rig-create step-status <step> <status> --by <who> [<project dir>] [--note-file <path>] [--json]
124
+ ```
125
+
126
+ `signoff` records approval, then `advance` records the move that approval
127
+ authorizes. The move's authority is the person's stage-level signature, so
128
+ `advance` takes no `--by` or words of its own (`ADVANCE_WORDS_REMOVED`): its
129
+ entry names the signature it rests on. The stage level signature closes its
130
+ named stage. `plan-approved` authorizes `parts`
131
+ for both the split-interview order and the legacy order that includes parts.
132
+ Only an already-passed pre-parts project keeps the historical route to rigging.
133
+ The legacy order omits `rigging-interview`; explicit adoption adds it.
134
+ A bar stage advances once every step it runs is done, using the preceding
135
+ stage-level signature as authority. All post items marked not applicable
136
+ allow `post-interview` to advance directly to `accepted`.
137
+
138
+ Arrival at `rigging-interview` or `post-interview` permits only the target
139
+ interview's silent-item `INTERVIEW_INCOMPLETE` findings, counted by code and
140
+ item path, so its answers can be recorded there. The ordinary check still
141
+ reports them, and approval or departure requires the answers.
142
+
143
+ The move from `parts` to `parts-review` also takes `--parts-check <report>
144
+ --parts <assembled dir>`, both project-relative: the `parts-check.json` that
145
+ `rig-parts check-parts` wrote for the assembled parts, retained in the project
146
+ the way the parts verdict is, and the root it was run on. Both are required on
147
+ that move and refused on any other. The report must pass (`exit_code` 0, no
148
+ `refusals`) and match the parts on disk at the move. Its HOLE rows, defect
149
+ classes parts cannot measure yet, are printed one named line each and stored;
150
+ a HOLE is not a pass and does not block (CHECKS.md § Parts-stage check at the
151
+ parts → parts-review move).
152
+
153
+ `advances[]` retains `{ stage, from_stage, at, source?,
154
+ steps_before, signoff, signoffs_before, order, post_not_passed, parts_check? }`,
155
+ with `parts_check { path, sha256, spec, parts, exit_code, holes, reported }` on
156
+ a `parts` → `parts-review` move, copied from the report and bound by its sha256. The clock supplies `at`,
157
+ and `signoff` is the stable `{ stage, checkpoint, at }` identity of the human approval the move rests on.
158
+ Entries written before advance stopped taking words also carry the mover's `by` and UTF-8 `verbatim`;
159
+ they stay valid when both are strings and `by` is not `agent`.
160
+ `steps_before` retains each step id, required phase and original status so a historical bar exit still requires completed work after replanning.
161
+ `signoffs_before` preserves the approval identities present at the move,
162
+ including tied timestamps. Reordering signatures preserves these references. `order` retains `legacy`, `split` or `pre-parts` so later
163
+ adoption preserves historical moves. `post_not_passed` captures whether every
164
+ post item was not applicable at the move. A historical post-interview to
165
+ accepted skip reads this boolean snapshot, so reopening and later applicable
166
+ post answers preserve the earlier authorized move. The current frontier still
167
+ uses current post answers. Forward history grants no reopen cutoff.
168
+ Earlier plan re-signatures do not revoke later approvals. Among live approvals,
169
+ the furthest closing stage supplies the frontier, using its latest signature
170
+ by time and then array position. Reopen and amendment rules still invalidate
171
+ approvals. An earlier re-signature leaves the later live stage target unchanged.
172
+
173
+ From `STAGE_PROGRESS_SINCE`, stage must match the latest stage-changing advance or reopen (or the initial stage without either). A mismatch is dated by the later of that move and the latest live signature at the furthest stage before the current one, so a hand move made before the contract stays a WARN after later sign-offs. An advance retains the stable signature that authorized it at that time, so later same-stage re-signatures preserve that move.
174
+
175
+ The checker pins both lag and skip from `STAGE_PROGRESS_SINCE`, dated by the
176
+ authorizing stage-level signature. Earlier readable dates WARN with the same codes. An unreadable frontier
177
+ date refuses with `STAGE_SIGNOFF_DATE_INVALID`. New advance writes enforce
178
+ lag and skip independently of signature age. Signoff retains its existing conditional, scoped candidate checks.
179
+ Its additional progress guard rejects a pre-existing refusal-level lag and
180
+ new candidate progress refusals. It permits only the lag introduced by its own
181
+ new frontier signature, awaiting the separate advance writer. Legacy WARN lag
182
+ remains permitted at signoff. A target can move only one ordered stage at a
183
+ time, including a permitted bar exit or the declared no-post route.
184
+
185
+ `step-status` runs in the step's execution stage and records one transition
186
+ through `pending` (also the absent status), `in-progress`, `at-checkpoint`,
187
+ then `done`. On an amended project the execution stage may also be the check's
188
+ `effective_stage` (Forward amendments), so the reset steps can be redone while
189
+ the stored stage is retained. On a legacy-order project whose plan was approved before
190
+ `PARTS_REVIEW_SINCE`, rig steps also run in `rigging`. That route grants no
191
+ authority of its own: the writer's candidate check still refuses a started
192
+ step without a live covering stage-level approval (`STEP_RUN_UNAPPROVED`), and
193
+ a `plan-approved` checkpoint signature is refused (`SIGNOFF_UNKNOWN_CHECKPOINT`). Each step's `status_history[]` retains
194
+ `{ from_status, status, by, at, checkpoint_required, signoff }`.
195
+ `--note-file <path>` optionally retains verbatim UTF-8 text as `note` and its byte
196
+ sha256 as `note_sha256`. The readable file must contain 1–1,000 bytes and no
197
+ control characters except newline. Both keys are absent without the flag; the
198
+ check refuses incomplete pairs or mismatched hashes. Notes do not change signed
199
+ step content.
200
+ `checkpoint_required` snapshots the resolved boolean requirement at that event.
201
+ History validates that snapshot when checking a done entry. Reopening and
202
+ replanning a checkpoint preserve earlier completion history. The current
203
+ writer still requires a live checkpoint signature whenever the current
204
+ checkpoint is enabled. Its `signoff` uses the same stable
205
+ identity object or null. A checkpointed `done` references
206
+ the latest matching live human checkpoint signature. Other transitions have
207
+ `signoff` null. Existing status-only history remains readable. Status and its
208
+ history stay outside the signed plan hash. Reopen continues to reset steps.
209
+ History must link: each advance or reopen leaves from the stage the previous
210
+ move reached (starting at `interview`), and each status entry starts from the
211
+ previous entry's status, or `pending` first and after a reopen or amendment
212
+ reset; the last entry matches `status`. A reopen's `steps_before` holds each
213
+ step's chained status at the reopen. Breaks are `STAGE_MOVE_UNLINKED`,
214
+ `STEP_STATUS_UNLINKED` and `REOPEN_STEP_UNLINKED`, dated by
215
+ `PROGRESS_HISTORY_SINCE` (CHECKS.md).
216
+ Both writers reconcile `stage_values` with the applicable order and run the
217
+ same complete `checkProject` candidate validation before writing. Refused
218
+ writes leave `project.json` byte-identical. Neither writer runs a producer.
219
+
220
+ ### Pausing and resuming
221
+
222
+ ```sh
223
+ rig-create signoff paused --by <who> --verbatim-file words.txt [<project dir>]
224
+ rig-create pause --by <who> [--verbatim-file <file>] [--source <text>] [<project dir>] [--json]
225
+ rig-create resume <stage> --by <who> [--verbatim-file <file>] [--source <text>] [<project dir>] [--json]
226
+ ```
227
+
228
+ A person stops the work by signing `paused`; `pause` then sets `stage` to
229
+ `paused`. It needs a human `{ "stage": "paused" }` sign-off dated at or after
230
+ the latest recorded move, so a second pause needs a new signature. `resume`
231
+ sets `stage` back to the stage the project was paused from, and refuses any
232
+ other target (`RESUME_STAGE_MISMATCH`). A project paused by hand before the
233
+ writer existed resumes at the stage its latest recorded move reached. Pausing
234
+ a paused project, resuming one that is not paused, and pausing at `accepted`
235
+ are refused by name. A reopen may also leave `paused`, as before.
236
+
237
+ Both append to `pauses[]`: `{ from_stage, stage: "paused", by, at, verbatim,
238
+ source?, signoff }` for a pause, with `signoff` the stable `{ stage,
239
+ checkpoint, at }` identity of the paused sign-off, and `{ from_stage:
240
+ "paused", stage, by, at, verbatim, source? }` for a resume. The clock
241
+ supplies `at`. The history is kept apart from `advances[]` and `reopens[]`,
242
+ so their linkage (`STAGE_MOVE_UNLINKED`) and the rule that `stage` equals the
243
+ latest recorded move read the same records as before: after a resume the
244
+ latest advance or reopen still names the stage, and a hand move to any other
245
+ stage is still `STAGE_SKIPPED`. The check validates the shape of each entry
246
+ (`PAUSE_MALFORMED`). Both writers run the complete `checkProject` candidate
247
+ validation before writing and replace `project.json` in one rename; refused
248
+ writes leave it byte-identical.
249
+
250
+ ### Reopening a passed stage
251
+
252
+ ```sh
253
+ rig-create reopen <stage> --by <who> [--verbatim-file <file>] [--source <text>] [<project dir>] [--json]
254
+ ```
255
+
256
+ A human may return to a previously reached stage without deleting signatures.
257
+ `reopen` appends `reopens[]` and sets `stage` to its target in the same validated
258
+ write. This backward move is the exception to `signoff`'s record-only writer:
259
+ leaving the old forward stage would require signatures the reopen supersedes.
260
+ The target is in the stage order, excluding `paused` and `accepted`, and no
261
+ later than the furthest stage evidenced by the current stage, historical human
262
+ sign-offs or dated rig/post versions. No producer is run.
263
+
264
+ Each entry is `{ stage, from_stage, by, at, verbatim, source?, steps_before }`. `at` comes
265
+ from the clock; words come unchanged from UTF-8 file or stdin (omitted or `-`).
266
+ `--source` separately records where the words came from. `from_stage` records
267
+ where the project stood when the reopen was written. Each entry's eligibility
268
+ uses its own `from_stage` and historical evidence dated no later than that entry,
269
+ so later backward moves neither erase nor manufacture its reach. An agent cannot sign.
270
+ `steps_before` snapshots `{ id, status }` for every plan step running in the
271
+ target stage or later, preserving an absent status by omitting it. Those steps
272
+ are reset to `pending`; their other fields stay unchanged. Status is outside
273
+ `step_hashes`, so existing plan hashes still verify. The append, resets and
274
+ stage move happen together, or the file is unchanged on refusal. Before writing,
275
+ the command runs the complete project check against the candidate record and
276
+ the existing project files; any refusal leaves the original bytes unchanged.
277
+
278
+ For each stage, a sign-off is live only if dated strictly after every reopen
279
+ whose target is that stage or earlier. Checkpoints follow the same rule;
280
+ undated entries cannot outrank a reopen. A second reopen advances that cutoff
281
+ without restoring signatures invalidated by an earlier, wider reopen. All
282
+ signatures remain in place, structurally checked as history. Advancing again
283
+ requires live signatures under the existing stage/checkpoint rules. A newly
284
+ run step without them is still refused; there is no execution exemption.
285
+
286
+ Accepted historical rig versions remain accepted as history. If the reopen
287
+ covers rig-review, a live hand-off requires a rig version built after it;
288
+ a new verdict on an old build cannot revive that build. Historical post
289
+ versions keep their original basis; new post work requires the live rig.
290
+ Reopening only post work preserves the unaffected rig acceptance.
291
+
292
+ ### Forward amendments
293
+
294
+ Raise a correction against an artifact and its owning stage with the public
295
+ writer. The stage is chosen explicitly by the person recording the correction.
296
+
297
+ ```sh
298
+ rig-create amend parts --kind part --target panel --change inputs/replacement.svg \
299
+ --by owner --verbatim-file correction.txt
300
+ ```
301
+
302
+ `amendments[]` retains `{ target, by, at, verbatim, source?, changes }`.
303
+ `target` contains `{ stage, kind, id }`, where `kind` is `part`, `config-field`,
304
+ `motion-track` or `interview-answer`. `id` names the artifact, config pointer,
305
+ track or answer path. Any ordered non-terminal stage may own a target.
306
+ `changes` contains `{ path, sha256 }` for every repeated `--change` file.
307
+ The writer reads and hashes those files under the project. These are declared
308
+ new inputs retained as historical byte identities. Checking history validates
309
+ their shape and does not require the mutable working files to retain those
310
+ bytes forever. `at` comes from the clock, and UTF-8 words are retained unchanged.
311
+ An agent signer is refused. Optional `--source` records the origin separately.
312
+
313
+ An amendment invalidates every approval at its owning stage or later, using
314
+ the same stage/time cutoff as `reopen`. The reason for each is exactly
315
+ `no binding proves independence`, with the fixed gap row below. Upstream means
316
+ every ordered stage preceding the amended stage. Its live approvals remain
317
+ live and its plan-step states are preserved. There is no additional unrelated
318
+ category. A downstream approval cannot stay live on this contract.
319
+
320
+ | Approval kind | Missing proof from the step-0 census |
321
+ | --- | --- |
322
+ | `interview` | G1, approved answer and input set |
323
+ | `plan-approved` | G2, answer contents and consumed inputs |
324
+ | `parts` checkpoint | G3, executed inputs and checked outputs |
325
+ | `parts-review` | G5, comparison inputs and rendered-stack input binding |
326
+ | `rigging-interview` | G2, answer contents and consumed inputs |
327
+ | `rigging` checkpoint | G3, executed inputs and checked outputs |
328
+ | `rig-review` signature or accepted verdict | G7, review target and evidence |
329
+ | `post-interview` | G2, answer contents and consumed inputs |
330
+ | `post` checkpoint | G3, executed inputs and checked outputs |
331
+ | `direction-review` signature or accepted verdict | G7, review target and evidence |
332
+ | An explicitly recorded `accepted` stage signature | G9, acceptance input closure |
333
+
334
+ The fixed table is `AMENDMENT_APPROVAL_GAPS` in `src/amend.ts`. The remaining
335
+ census rows cover producer derivation (G4), approval granularity (G6), field
336
+ consumption (G8), build closure (G9) and opaque producer resolvers (G10).
337
+ This table identifies missing proof for the approval kind. It does not assert
338
+ that one gap alone suffices to prove independence.
339
+
340
+ The extension point is `keepByBinding()` and the report field is
341
+ `keep_by_binding`. It ships as an empty list. Later binding cards may place an
342
+ approval there with the binding that proves its independence. Until then the
343
+ amendment's stage and downstream approvals all lapse.
344
+
345
+ `interview --check` adds `amendments[]` only on amended records. Each entry
346
+ names the target, every invalidated signature and accepted verdict by record
347
+ path, its stage/checkpoint, reason and gap, the still-live upstream approvals,
348
+ and the empty keep-by-binding list. A new signature remains a separate act
349
+ from the lapsed historical one. Multiple amendments and reopens accumulate
350
+ cutoffs, so a narrower amendment cannot revive an earlier invalidation.
351
+
352
+ The writer leaves `project.stage` in place, sets plan steps running at or after
353
+ the amended stage to `pending`, and retains all previous approvals and versions.
354
+ The check's `effective_stage` is the first unfinished stage while re-approval
355
+ is pending, bounded by the stored stage. While it differs from the stored
356
+ stage, the check still refuses a stage that differs from its latest recorded
357
+ move (`STAGE_SKIPPED`, same dating), but skips the signature window
358
+ (`STAGE_LAGGING` and the window's `STAGE_SKIPPED`). The stored stage stays
359
+ while its approvals lapse, so the window would refuse every amended record,
360
+ the amend writer's own candidate included. Judging the window against
361
+ `effective_stage` instead fails too: that stage passes unfinished bars before
362
+ the amended stage, which the window stops at. Re-sign the lapsed approvals and meet
363
+ the affected steps' bars in order. `step-status` runs a reset step when its
364
+ execution stage is the stored stage or the `effective_stage`, which it reads from
365
+ the same complete check, so the bars are met in order without moving `stage`.
366
+ `effective_stage` comes from the recorded signatures and step statuses; it is not
367
+ stored, because each re-signature or finished bar moves it. Once every lapsed
368
+ approval is re-signed and every bar is met, `effective_stage` equals the stored
369
+ stage again and the signature window applies as before; no `advance` is needed
370
+ when the stored stage was already reached. Existing upstream approvals need no re-sign.
371
+ A rig covered by the cutoff needs a new build and acceptance before handoff.
372
+ The writer runs the same complete candidate check before writing. Any refusal
373
+ leaves `project.json` byte-identical. Existing records and `reopen` output retain
374
+ their existing shape when no amendment is present.
375
+
376
+ ### The parts are reviewed before rigging
377
+
378
+ `parts-review [<project>] --draft <id> --from-review-cli --by <name>
379
+ [--json] -- <rig-review parts arguments>` records an independent parts verdict
380
+ through `rig-review parts <arguments> --format json`. Put create's flags before
381
+ `--`. The remaining arguments, including `--verdict`, are opaque strings passed
382
+ to review in the project directory. They must be project-relative and stay
383
+ inside the project. create reads only stdout and never opens the saved verdict
384
+ named by those arguments. A failed CLI call refuses with its diagnostic.
385
+
386
+ Exact stdout bytes are retained at `evidence/review-reads/<sha256>.json`.
387
+ The ordered `review_acts` list holds both routes. A CLI receipt uses schema
388
+ `rig-create/parts-verdict-cli@1`, `kind: "parts-verdict"`, `route: "cli"`,
389
+ `draft_id`, `argv`, `evidence: {path, sha256}`, `tool: {name, version}`,
390
+ `status`, `flagged`, `noted`, `by` and `at`. Names retain their response order
391
+ and spelling. Notes remain verbatim in the retained response. `--by` identifies
392
+ the human because the CLI response supplies no reviewer. Approved, pending and
393
+ changes-requested responses can all be recorded. Recording identical bytes
394
+ for the same draft again preserves the record byte for byte. Different bytes
395
+ append, and the last receipt for that draft is current across both routes.
396
+ A current changes-requested receipt refuses `signoff --draft` until a newer
397
+ approved receipt exists. This does not itself sign or move a stage.
398
+
399
+ The writer and check compare the CLI source hash multiset with the draft's
400
+ recorded parts and output hashes. Every reviewed hash must be recorded and
401
+ every part must be reviewed. Outputs absent from the response are optional.
402
+ The painting declared by the draft's hash-bound `parts.json` is excluded on
403
+ both sides, including repeated occurrences of its hash. Response paths are
404
+ never resolved. Check reads the retained stdout, verifies its hash and copied
405
+ receipt fields, and repeats this draft binding without querying the saved
406
+ verdict. Signatures before `PARTS_VERDICT_LATEST_SINCE` warn for a current
407
+ changes-requested receipt, while signatures from that date refuse.
408
+
409
+
410
+ A defect in the parts — a part with nothing painted under its occluder, a
411
+ boundary between two parts nobody composed — is cheapest to fix before a bone
412
+ is placed on it. Found at `rig-review`, every build in between was spent on
413
+ parts that were already wrong, and the fix goes back through the split and
414
+ every step after it. So the steps that make the parts (`seethrough`,
415
+ `assemble`) run in their own stage, `parts`, and a person looks at the
416
+ assembled parts before `rigging` starts.
417
+
418
+ - **`parts` ends on its bar**: the parts check's gates pass on the assembled
419
+ parts, recorded as `assemble`'s bar met (`status: "done"`). Coverage under
420
+ occluders joins this bar from rig-c/parts#998 once it ships; until then the
421
+ bar is the parts check as it is. A project past `parts` with one of its steps
422
+ not `done` is refused (`PARTS_BAR_UNMET`). The move to `parts-review` binds
423
+ the passing `rig-parts check-parts` report for the assembled parts
424
+ (`advances[].parts_check`, § Recording forward progress); its HOLE lines
425
+ reach the person at `parts-review` and are recorded on the move.
426
+ - **`parts-review` ends on a human sign-off**, `{ "stage": "parts-review",
427
+ "checkpoint": null }`, given in rig-review's parts view. Without it the stage
428
+ does not move to `rigging`, and no `rigging` step runs
429
+ (`PARTS_REVIEW_UNSIGNED`). An agent's entry does not count.
430
+ - **The sign-off carries the verdict it rests on**: `"evidence": { "kind":
431
+ "parts-verdict", "path": "<the file>", "sha256": "<its sha256>" }`, where
432
+ `path` is relative to the project and names the file rig-review's parts view
433
+ wrote (`rig-review parts`; by default `parts-review.json` beside
434
+ `parts.json`, or wherever `--verdict` put it). Write it to a tracked path,
435
+ `--verdict evidence/parts-review.json`, so the sign-off's evidence survives a
436
+ clone: `derived/`, where the parts are assembled, is not in git. The check
437
+ reads exactly that file, never a default location. Rigging is refused, each case by name
438
+ ([CHECKS.md § Refusals](CHECKS.md#refusals)), unless the verdict is the
439
+ `rig-review-parts-verdict/1` or `rig-review-parts-verdict/2` schema in `parts` mode (an `atlas` verdict is
440
+ informational and never gates), its `status` is `approved`, and every file
441
+ in its `source.files[]` is on disk with the bytes and sha256 it was reviewed
442
+ with. A part added, removed or repainted after the sign-off is a new review:
443
+ the user reviews again and signs again, with the new verdict as evidence.
444
+ The latest `parts-review` sign-off is the one read. **The parts themselves
445
+ must be on disk to rig**: on a fresh clone, re-assemble them before rigging
446
+ continues. The verdict gates rigging only until a rig version is accepted at
447
+ `rig-review`: from then on it is not read, and a part changed after that is a
448
+ new rig version, refused as `RIG_VERSION_UNACCEPTED` until it is reviewed.
449
+
450
+ Both schemas take the same approval, mode, evidence SHA-256, source path,
451
+ byte-count and source SHA-256 checks. The schema allowlist is closed: `/3`
452
+ and any other value refuse `PARTS_VERDICT_SCHEMA_UNKNOWN`. The signoff writer
453
+ calls this reader before writing; the difference-map reader receives only
454
+ its successfully checked `parts.json` path. These readers do not need new
455
+ fields (`tests/parts-verdict-v2.test.ts`).
456
+
457
+ **Fields added by `/2`.** The schema-final review writer at
458
+ [`07c07d254725c97cfc63aa7b028cb8ef8ccf9045`](https://github.com/rig-c/review/blob/07c07d254725c97cfc63aa7b028cb8ef8ccf9045/src/parts-view.ts)
459
+ adds `comments[]`; its complete nested shape comes from
460
+ [`PartsComment`](https://github.com/rig-c/review/blob/07c07d254725c97cfc63aa7b028cb8ef8ccf9045/src/parts-comment.ts).
461
+ All fields below are explicitly ignored **for create's gate**, retained
462
+ unchanged in the verdict file and covered by `evidence.sha256`. create
463
+ neither copies nor rewrites them into `project.json`, and does not open crop
464
+ paths or require the review machine's crop state. Review owns comment
465
+ validation and crop generation; an approved verdict may include comments.
466
+
467
+ | New field paths | Why create does not interpret them |
468
+ | --- | --- |
469
+ | `comments` (including an empty list), `comments[].schema`, `.id`, `.summary` | Reviewer annotations, not a second approval status. The gate reads the producer's top-level `status`. |
470
+ | `comments[].part`, `.imageHash`, `.sourceHash` | Comment-specific identities owned by review. The gate binds all original source bytes through `source.files[]` and `source.sha256`, regardless of comments. |
471
+ | `comments[].imageRegion.{x,y,width,height}`, `.rigRegion.{x,y,width,height}` | Native and placed annotation coordinates; create consumes no region measurement and must not rederive it. |
472
+ | `comments[].view.camera.zoom`, `.camera.center.{x,y}`, `.camera.viewport.{width,height}` | Primary viewport camera, used to replay the review view, not to approve parts. All camera paths in this row are under `comments[].view`. |
473
+ | `comments[].view.cameraSync`, `.referenceCamera` (nullable), `.referenceCamera.zoom`, `.referenceCamera.center.{x,y}`, `.referenceCamera.viewport.{width,height}` | Reference viewport replay and synchronization; no camera consumer in this gate. |
474
+ | `comments[].view.{background,alpha,footprints,mesh}` | Display toggles, not part outcomes or producer measurements. |
475
+ | `comments[].crop.{path,sha256,width,height,png}` | Viewport crop metadata and inline PNG, owned by review; not the assembly source inventory. |
476
+ | `comments[].nativeCrop` (nullable in the type), `.nativeCrop.{path,sha256,width,height}` | Review-generated native crop evidence, not a replacement for the original part image. create binds the original bytes without regenerating a crop. |
477
+
478
+ `tests/fixtures/rig-review-parts-verdict-2/README.md` records the exact producer
479
+ head and reproducible command. Its empty-comment and pointed-comment verdicts
480
+ are unedited writer output. `tests/parts-verdict-v2.test.ts` exercises both
481
+ through `signoffCommand` then `checkProject`, tests comment-byte binding and
482
+ operation without external crop state, and applies source/mode refusal
483
+ controls to both schema versions. Existing `/1` fixture bytes are unchanged.
484
+
485
+ - **The project record carries an agent-authored whole-figure difference map**
486
+ at `project.json.diff_map`, while the `parts-review` sign-off records only the
487
+ person's approval (`draft_id`, `evidence`, `structure`, `verbatim`).
488
+ Generate the report with the installed `rig-parts diff-components --out` on
489
+ the assembled stack and the painting in the same native canvas, then record
490
+ the agent's component classifications with `rig-create diff-classes`. create
491
+ reads the report and binds its exact bytes by SHA-256.
492
+
493
+ ```json
494
+ "diff_map": {
495
+ "by": "agent",
496
+ "at": "2026-11-09T00:00:50.000Z",
497
+ "report": { "path": "evidence/diff-components.json", "sha256": "<report sha256>" },
498
+ "components": [ { "id": "<report component id>", "status": "in-painting", "why": "<what was observed>" } ]
499
+ }
500
+ ```
501
+
502
+ ```sh
503
+ rig-create diff-classes --diff-report evidence/diff-components.json --component 'p10-n3=in-painting:the painting contains this edge' --by agent
504
+ rig-create signoff parts-review --by user --draft <id> --structure-file evidence/structure-decisions.json --verbatim-file approval.txt
505
+ ```
506
+
507
+ The `diff-classes` writer hashes the report and validates the entire map
508
+ before recording anything (`by` must be `agent`, refused otherwise with
509
+ `DIFF_MAP_NOT_AGENT`). Repeat `--component <id>=<status>:<why>` for every
510
+ component. The first `=` and first following `:` separate fields, so `why` may
511
+ contain colons. Every report id must appear exactly once, with no extra ids.
512
+ Status is `in-painting`, `fixed` or `left`, and every status requires a
513
+ nonempty `why`. For a large report, run `rig-create diff-classes --diff-report <path>`
514
+ without `--triage-file`, `--component` or `--by` first: in read-only mode it
515
+ prints JSON rows with deterministic ids, derived keys, member ids and counts,
516
+ pixel-count ranges and uncovered-fraction ranges. Keys use the exact sorted
517
+ border-part set and coverage band. Covered and uncovered bands have fractions
518
+ 0 and 1; mixed bands are [0,0.25), [0.25,0.50), [0.50,0.75), [0.75,1).
519
+ This caps mixed spread below 0.25 without combining different border sets.
520
+ create groups producer fields; it never measures image pixels.
521
+
522
+ Supply `--triage-file triage.json` to `diff-classes --diff-report <path> --by agent`
523
+ instead of component flags:
524
+ `{"classes":{"<derived id>":{"status":"left","why":"covered edge observed in the painting"}},"components":{}}`.
525
+ Each decision contains only status and why; the writer derives the key and full
526
+ member list. A class reason must name at least one exact border part or its
527
+ coverage band (for example `mixed:0.25-0.50`). Inspect the images before deciding.
528
+ Components needing individual judgment can instead use the `components` map,
529
+ keyed by report id with the same status/why fields. Do not overlap decisions.
530
+ The file cannot be combined with component flags and requires a diff report.
531
+
532
+ The optional `diff_map.classes` list retains `{id, key, status, why, members}`;
533
+ each expanded component also retains `class_id`. Check re-derives each full
534
+ class and requires disjoint members and identical expanded status and why.
535
+ Per-component records remain compatible and do not acquire class requirements.
536
+
537
+ A report with no components needs only `--diff-report <path> --by agent`. Its spec must be
538
+ `rig-parts-diff-components/1`, `definition.floor_px` at most 2 and
539
+ `definition.threshold` at most 40. The stack is derived from the verdict's
540
+ covered `parts.json`, resolved relative to that verdict (or the latest draft
541
+ before sign-off). For its directory D, build writes `D/recomposite_rig.png`,
542
+ while standalone assemble writes `D/../render/recomposite_rig.png` only when D
543
+ is named `rig`. Exactly one candidate must exist and hash to the report's
544
+ `stack.sha256`. Neither or both candidates refuse rigging. create never
545
+ computes the map or runs the producer's `--verify`. This uses the latest
546
+ sign-off and the same rigging gate and acceptance boundary as the parts
547
+ verdict above. From `DIFF_MAP_SINCE` (2026-11-06T00:00:00Z), a missing map
548
+ refuses writing the sign-off and rigging. An earlier sign-off without one gets
549
+ only `DIFF_MAP_PREDATES`. From `DIFF_MAP_AGENT_SINCE` (2026-11-09T00:00:00Z),
550
+ `signoff parts-review` refuses `--diff-report`, `--triage-file` and
551
+ `--component` (`SIGNOFF_DIFF_MAP_REMOVED`), and a `parts-review` sign-off dated
552
+ from that boundary carrying `diff_map` is refused (`SIGNOFF_DIFF_MAP_REMOVED`);
553
+ an earlier `parts-review` sign-off carrying `diff_map` emits a
554
+ `DIFF_MAP_ON_SIGNOFF_LEGACY` warning and its map is still checked in full.
555
+
556
+ The filled example's report and stack are synthetic, with two listed
557
+ components. `bun scripts/generate-example-diff.ts <fresh scratch directory>`
558
+ regenerates them through the installed parts CLI and the `diff-classes` and
559
+ `signoff` writers. Add `--check` to reproduce the shipped report, stack and
560
+ inline record bytes.
561
+ - **A project that passed `plan-approved` before these two stages existed**
562
+ (its first `plan-approved` sign-off is dated before 2026-11-04, the
563
+ `PARTS_REVIEW_SINCE` in `src/stages.ts`) is not given a sign-off nobody made.
564
+ The check reports it as a warning naming the project
565
+ (`PARTS_REVIEW_PREDATES_STAGE`): its parts were not reviewed before rigging.
566
+ Its `assemble` checkpoint, signed then as `rigging`, still counts. The date
567
+ is read from that sign-off because signing `plan-approved` is the dated,
568
+ human act that moved the project past where the two stages now stand.
569
+
570
+ ### The rig review rests on a still frame
571
+
572
+ The rig is accepted at `rig-review` by **one verdict on one version**: a
573
+ human `verdicts[]` entry with `"review": "rig-review"` and `"verdict":
574
+ "accepted"` on a `kind: "rig"` version. The `{ "stage": "rig-review" }`
575
+ sign-off moves the stage; the verdict names the version. They are the same
576
+ act and are written in the same turn (the sign-off has no version field
577
+ because the verdict already carries `by`, `at` and `verbatim` on the version
578
+ it is about — a second copy would be a second place for one decision).
579
+
580
+ That acceptance rests on a **still-frame record** in the same version's
581
+ `evidence[]`:
582
+
583
+ ```json
584
+ { "kind": "still", "path": "evidence/v2/still-frame.png", "sha256": "…", "camera": "fixed", "frame": 2, "what": "the rig at the fixed camera, posed 2 s into the loop" }
585
+ ```
586
+
587
+ | Field | Holds |
588
+ | --- | --- |
589
+ | `kind` | `"still"` — what marks this entry as the rig review's frame; other evidence entries may leave `kind` out |
590
+ | `path`, `sha256` | the image under `evidence/v<N>/` and its 64-hex sha256 |
591
+ | `camera` | `"fixed"`: no camera move, no parallax, no post-production; any other value is not a still frame |
592
+ | `frame` | `"setup"` for the renderer's setup pose, or the time in seconds into the motion the rig is posed at. `0` is the motion's start, which is not the setup pose when the idle rests off it (a breath track whose `base` equals its `amp` passes through setup mid-loop) |
593
+ | `what` | what it shows, in words |
594
+
595
+ This repository does not render the still: rig-c/core and rig-c/review render,
596
+ the `review <version> --from-review <actId>` writer fetches the
597
+ raw act bytes and PNG through the live job API. The writer preserves the act bytes under
598
+ `evidence/v<N>/`, binds them in `review_acts` by sha256 and verifies the PNG
599
+ before recording it. Missing or mismatched PNGs refuse without changing
600
+ `project.json`. Numeric frames record the animation name in `what`;
601
+ no new record field or dated contract is introduced. An acceptance with no such record is refused
602
+ (`RIG_ACCEPTED_WITHOUT_STILL`, [CHECKS.md](CHECKS.md)); a bounce needs none to
603
+ be recorded, though what it rested on still goes in `evidence/`.
604
+
605
+ **A rig changed after `rig-review`** is a new rig version, and the project is
606
+ back at `rig-review` for it: past that stage, the latest rig version must carry
607
+ its own acceptance (`RIG_VERSION_UNACCEPTED`). Once it is accepted, the post
608
+ version built on the old rig is redone on the new one — the current (latest)
609
+ post version naming an older accepted rig is refused (`POST_ON_SUPERSEDED_RIG`);
610
+ earlier post versions are history and are left as they are. This is a refusal,
611
+ not a warning: a direction review of a take built on a rig the user has since
612
+ replaced would accept something that no longer matches the rig.
613
+
614
+ ### The hand-off to post-production
615
+
616
+ ```
617
+ rig-create handoff [<project dir>] [--rig <n>] [--json]
618
+ ```
619
+
620
+ Post-production tooling runs this before it runs. It exits `0` and prints the
621
+ one rig version post work may run on — the latest accepted rig version, with
622
+ its acceptance and its still frame — and exits `1` with a named refusal when
623
+ there is none: no `rig-review` sign-off (`RIG_REVIEW_UNSIGNED`), no accepted
624
+ rig version or a newer rig version not yet reviewed (`RIG_VERSION_UNACCEPTED`),
625
+ an acceptance with no still frame (`RIG_ACCEPTED_WITHOUT_STILL`). With
626
+ `--rig <n>` it also refuses when `<n>` is not that version
627
+ (`POST_ON_UNACCEPTED_RIG`, `POST_ON_SUPERSEDED_RIG`). The `n` it prints is what
628
+ the post version's `rig_version` holds. It reads the record and writes nothing.
629
+
630
+ **When post is not passed.** If every post item is answered *not applicable* in
631
+ `post-interview` (a collection member whose composition belongs to the
632
+ collection, for example), the sign-off of `post-interview` says so and the
633
+ project moves from there to `accepted`.
634
+
635
+ ### Split approval is a plan checkpoint
636
+
637
+ The split — which parts the painting becomes — is the decision most often sent
638
+ back, so it is shown to the user as a labelled image before the rest of the rig
639
+ is built. It is a **checkpoint on the `assemble` step**, not a stage of its own:
640
+ a stage and a checkpoint are the same act (a human signature before the next
641
+ piece of work runs), so keeping both would record one approval in two places,
642
+ and the plan lets the user stop at any waypoint, not only the split. The split's
643
+ data (proposal image under `evidence/split/`, parts, labels) lives in `split`;
644
+ its approval is the `signoffs[]` entry with `"stage": "parts", "checkpoint": "assemble"`. This checkpoint has no
645
+ default: the user turns it on or off, or delegates it, like every other.
646
+
647
+ ### Generated split captions
648
+
649
+ ```sh
650
+ rig-create split-captions [<project dir|project.json>] [--config <file>]
651
+ ```
652
+
653
+ The command prints `rig-create/split-captions@0` JSON to stdout. A directory
654
+ uses its `project.json`, and the default is the current directory. Config
655
+ paths are relative to the project record's directory, defaulting to
656
+ `config.json`. Redirect the result into the proposal's evidence directory.
657
+
658
+ `inputs` records the project and config paths and SHA-256 of their exact bytes.
659
+ `config[]` carries each top-level setting's `path`, structured `value` and
660
+ machine-generated `text` (`path = JSON(value)`), including the assembly scale,
661
+ plan, limb routes and bone bindings. `parts[]` preserves `split.parts` order
662
+ and carries each `name`, `label` and generated `text` (`JSON(name) = JSON(label)`).
663
+ A sheet renderer can use those strings directly. Every caption comes from the
664
+ input data. Config annotations (`note`, `*_note`, `x-*`) are omitted, preserving
665
+ exact-name map keys, including limb names, turn depth part names and depth
666
+ source-layer names. `split.parts.note` is excluded.
667
+
668
+ The installed parts parser validates complete rig configs through its full
669
+ config door, and incomplete configs through its early `assemble` door, which
670
+ checks the split's settings and reads bones with declared limbs. The output
671
+ records declarations. Pixel assembly, rendered scale and motion acceptance
672
+ require their own evidence. Exit 1 names an unreadable or malformed input,
673
+ unknown project schema, malformed or duplicate split name, invalid label, or
674
+ parts config refusal. Usage errors exit 2. Refusals produce no caption stdout.
675
+
676
+ The worked list is [example-split/captions.json](../templates/example-split/captions.json),
677
+ generated with the existing filled record and a synthetic config. Regenerate it
678
+ from the repository root with
679
+
680
+ ```sh
681
+ bun src/cli.ts split-captions templates/example-project.json --config example-split/config.json > templates/example-split/captions.json
682
+ ```
683
+
684
+ [example-split/config.json](../templates/example-split/config.json) supplies
685
+ synthetic config bytes because the filled record's historical config paths
686
+ have no corresponding files. Its scale, coordinates, source tags, bindings and
687
+ empty motion tracks are test inputs. The painting, split layers and rig output
688
+ remain unverified by this caption fixture.
689
+
690
+ ## The plan
691
+
692
+ The plan lives in `project.json` `plan`, not in a separate file: `project.json`
693
+ is already the only place a decision is recorded and the first thing a resuming
694
+ agent reads, and a second file is one a resume can skip.
695
+
696
+ ```json
697
+ "plan": {
698
+ "drafted_by": "agent",
699
+ "drafted_at": "2026-11-10T15:30:00Z",
700
+ "steps": [
701
+ {
702
+ "id": "assemble",
703
+ "phase": "rig",
704
+ "order": 2,
705
+ "does": "Turn layers into parts, in draw order; propose the split as an image, one label per part",
706
+ "produces": "config assemble; split.proposal_image, split.parts[]",
707
+ "rests_on": ["assemble", "constraints"],
708
+ "bar": "Every part the assemble guide names exists; every part has one label; the setup-pose recomposite matches the painting",
709
+ "checkpoint_proposed": true,
710
+ "checkpoint": { "delegated": false, "by": "user", "at": "2026-11-10T16:00:00Z", "verbatim": "yes, show me", "value": true },
711
+ "status": "pending"
712
+ }
713
+ ],
714
+ "replaced": [
715
+ {
716
+ "step": {
717
+ "id": "camera",
718
+ "phase": "post",
719
+ "order": 1,
720
+ "does": "Keep a fixed frame.",
721
+ "produces": "A fixed camera track.",
722
+ "rests_on": ["camera_depth"],
723
+ "bar": "The approved rig stays within the frame.",
724
+ "checkpoint_proposed": false,
725
+ "checkpoint": null,
726
+ "status": "pending"
727
+ },
728
+ "sha256": "1f42b6a62120e479ea30aeb08244d5c35b8b4c3ec803f079489047d9e8f4bd61",
729
+ "action": "removed",
730
+ "at": "2026-11-10T15:30:00Z",
731
+ "by": "agent"
732
+ }
733
+ ]
734
+ }
735
+ ```
736
+
737
+ Draft steps with `rig-create plan add|revise|remove <id> [<project dir>]
738
+ --by <drafter>`. Its `--help` lists their field flags. Draft dates follow the
739
+ existing [phase-date contract](#explicit-adoption-of-the-split-interview),
740
+ using both previous and resulting approval ownership on revision, with
741
+ `drafted_by` updated only alongside `drafted_at`. Every write refuses
742
+ additional checker findings counted by code and path before changing the file. Reopen live covering plan
743
+ approvals first. Completed work, including completion retained by reopen,
744
+ requires removal and a new id. Revise/remove append the full previous step
745
+ to optional `replaced[]`, with its existing `stepHash`, action, ISO date and
746
+ drafter. Entries retain write order, nondecreasing dates and unique
747
+ `step.id` / `at` identities. Reopened signatures may resolve hashes and
748
+ checkpoints against history. Live approvals bind current steps.
749
+
750
+ - **Rig steps follow the waypoints**: one step per waypoint the plan passes, in
751
+ pipeline order, with the step's `id` equal to the waypoint's. The last rig
752
+ step also renders the still-frame check for `rig-review`. **Post steps**
753
+ (`phase: "post"`) are added in `post-interview`, one per post item passed.
754
+ - **`rests_on` names the answers a step is built on** — waypoint ids, framing
755
+ ids (`rig_type`, `reference`) or post item ids; at least one. A step no answer
756
+ covers is not allowed: ask for the sentence first
757
+ ([INTERVIEW.md § Questions outside this set](INTERVIEW.md#questions-outside-this-set)).
758
+ A step resting on a delegation is legal; the delegation is read from the
759
+ guide, not restated here.
760
+ - **`checkpoint`** takes the answer shape from INTERVIEW.md: the user's `true` /
761
+ `false` as `value`, or delegated. The drafter may fill `checkpoint_proposed`;
762
+ only the user (or a delegation) fills `checkpoint`.
763
+ - **`bar`** is what *done* is measured by for that step, written before the
764
+ step runs. A sentence that names a part, effect or extent is a bar line
765
+ ([INTERVIEW.md § Direction is taken in sentences](INTERVIEW.md#direction-is-taken-in-sentences)).
766
+ - **`status`**: `pending` → `in-progress` → `at-checkpoint` (waiting for a
767
+ sign-off) → `done`.
768
+ - A changed guide — including one added from polish feedback — sends every step
769
+ that rests on it back for a new sign-off by its step-specific approval stage before it runs.
770
+ - **Provenance**: each version built from a config records, per config
771
+ section, the guide sentence or delegation each entry follows
772
+ (`versions[].provenance`, [INTERVIEW.md § Provenance](INTERVIEW.md#provenance-every-config-section-cites-its-guide)).
773
+ An entry with no citation is removed or cited before the version is shown.
774
+
775
+ ## Recording a sign-off
776
+
777
+ ```sh
778
+ rig-create signoff <stage> --from-review <actId> [<project dir>] [--json]
779
+ rig-create signoff <stage> --by <who> [--steps <id,id,...>] [--verbatim-file <file>] [<project dir>] [--json]
780
+ ```
781
+
782
+ The `signoff` command appends `signoffs[]` and leaves `project.stage` unchanged. `at`
783
+ comes from the clock. There is no `--at` option. The user's UTF-8 words come
784
+ from the file, or stdin when omitted or `-`, preserving BOM, spaces, CRLFs and
785
+ final newlines. Invalid UTF-8 is refused because a JSON string cannot retain
786
+ its original bytes.
787
+
788
+ `--from-review <actId>` without `--checkpoint` is the review tool's stage
789
+ sign-off route (with `--checkpoint <id>` it records a passed checkpoint act
790
+ instead; see CHECKS.md). It fetches the person's
791
+ finalized `signoff` act through the live job writer binding, the way
792
+ `review --from-review` and `interview-answer --from-review` do, and validates
793
+ it with the same decoder the check runs on the retained act bytes. The act
794
+ supplies the stage (it must be the one named), the words, the person (`by` is
795
+ `user` for the job owner, `delegate` otherwise) and `at`; `--by`,
796
+ `--verbatim-file` and `--steps` are refused with it on both routes
797
+ (`SIGNOFF_OPTION_INVALID`). For `plan-approved`, `rigging-interview` and
798
+ `post-interview` the act's `source.plan` must list the current `plan.steps`
799
+ ids and step hashes in order, or the write refuses `REVIEW_ACT_SOURCE_STALE`;
800
+ coverage and `step_hashes` then follow the writer's usual rules. For
801
+ `rig-review` and `direction-review` the act's `source.selector` names the
802
+ version the person was shown (a `rig` version, or a `post` version), and that
803
+ version is the signature's `target`; `--version` may only repeat it. A missing
804
+ or other-kind selector, or a different `--version`, refuses
805
+ `REVIEW_ACT_TARGET_MISMATCH`. The signature
806
+ names its act in `source` (`review act <jobId>/<actId>`), and `review_acts[]`
807
+ retains `{ kind: "signoff", jobId, actId, stage, bytes, sha256, version_hash }`.
808
+ Replaying the same act returns the recorded signature unchanged.
809
+
810
+ For new-order `plan-approved`, coverage is the parts steps; `rigging-interview` covers the remaining rig steps. Legacy `plan-approved` covers every rig step. For `post-interview`, it is
811
+ every post step. `--steps` narrows coverage within that phase and stores the
812
+ covered ids in `steps[]`. Execution stages accept `--checkpoint <step id>`
813
+ when that step runs in the
814
+ named stage and its recorded checkpoint is enabled. This records
815
+ `checkpoint: <step id>`, without `steps` or `step_hashes`. It cannot be combined
816
+ with `--steps`.
817
+
818
+ `parts-review` requires `--evidence <verdict path>`. The command stores
819
+ `{ kind: "parts-verdict", path, sha256 }`, resolving filesystem links,
820
+ normalising the path to project-relative POSIX form and hashing the file. The existing parts-verdict reader validates
821
+ approval, shape and source freshness before anything is written. Other stages
822
+ refuse `--evidence`.
823
+
824
+ Only `plan-approved`, `rigging-interview` and `post-interview` accept `--steps`. Stage movement
825
+ remains a separate act read by the check.
826
+
827
+ Each covered step needs a boolean `checkpoint_proposed`. A null checkpoint
828
+ becomes `{ delegated: false, by, at, verbatim, value: checkpoint_proposed }`.
829
+ Existing user answers and decided delegations stay as recorded. An undecided
830
+ delegation, malformed checkpoint or unknown step refuses the whole command
831
+ before the file is written.
832
+
833
+ Each plan signature also records `step_hashes`, mapping covered step ids to
834
+ SHA-256 of UTF-8 JSON with object keys recursively sorted and array order
835
+ preserved. The fields are `id`, `phase`, `order`, `does`, `produces`,
836
+ `rests_on`, `bar`, `checkpoint_proposed` and `checkpoint`, after filling the
837
+ checkpoint. Absent fields are omitted. `status` and other fields are excluded.
838
+ The shared implementation is `src/step-hash.ts`.
839
+
840
+ The four answer approvals (`interview`, `plan-approved`, `rigging-interview`,
841
+ `post-interview`) also record `answer_hashes`, mapping item ids to SHA-256 of
842
+ UTF-8 `JSON.stringify(sorted({ raw, resolved }))`: the local answer object
843
+ and the answer resolved through collection inheritance (or null). Object keys
844
+ are recursively sorted; array order is preserved. Both objects exclude
845
+ `history`, `prompts`, `config`, `text`, `source` and `required_for`.
846
+ The interview approves answers and delegations without `agent_decision`; plan
847
+ approvals also bind the decisions their covered steps rest on.
848
+ The writer captures every currently answered interview item, or the union of
849
+ `rests_on` in its covered steps; an unanswered step basis refuses the write.
850
+ An honestly empty set records `{}`. When every post item is N/A and there are
851
+ no post steps, the user still signs post-interview with `steps: []` and bindings
852
+ for every N/A post answer. Interview completeness requires a map,
853
+ without comparing its keys to today's answered items or their dates.
854
+ The latest live interview signature replaces the whole earlier interview map,
855
+ so omitted unanswered items lose approval and earlier interview bindings are superseded.
856
+ Plan-stage signatures supersede earlier bindings per item within the same stage,
857
+ while completeness still follows each signature's own covered steps.
858
+ Only live approvals are checked, using the same reopen and amendment liveness
859
+ as other approvals. A changed or removed bound item refuses
860
+ `SIGNOFF_ANSWERS_STALE`; an absent or incomplete map refuses
861
+ `SIGNOFF_ANSWERS_MISSING` from `SIGNOFF_ANSWERS_SINCE = 2026-11-06T00:00:00Z`,
862
+ and earlier signatures receive one `SIGNOFF_ANSWERS_MISSING_LEGACY` warning.
863
+ These bindings do not preserve approvals during amendments: G2 lapse behavior
864
+ and `keep_by_binding: []` remain unchanged until G8 is implemented.
865
+
866
+
867
+ `review` accepts `--verbatim-file <file>` (or `-` for stdin) instead of
868
+ `--verbatim`, and `--at <timestamp>` instead of the clock. Repeatable
869
+ `--answers-feedback <version>:<verdict-index>` appends references to the
870
+ reviewed version's `answers_feedback`. Each reference must identify an
871
+ existing verdict of an earlier version and must not duplicate an existing or
872
+ new reference. Both writers refuse `by: agent` case-insensitively.
873
+
874
+ ## Versions
875
+
876
+ A reopen preserves every version and verdict; a rebuild is a new numbered
877
+ version, never an overwrite of an accepted historical build.
878
+
879
+ After a human bounce, `next` reads check's `FEEDBACK_OPEN` finding and names
880
+ the agent rebuild instead of asking for another verdict or sign-off on the
881
+ bounced version. `rig-version` and `direction write` / `direction patch`
882
+ accept repeatable `--answers <version>:<verdict-index>` options, recording
883
+ those entries in the new version's `answers_feedback`. Each entry must name
884
+ an earlier human bounced verdict and must occur only once. Without the needed
885
+ entries the writer refuses `FEEDBACK_UNANSWERED`, leaving `project.json`
886
+ byte-identical. Existing historical references and `review --answers-feedback`
887
+ keep their earlier target rules: any existing verdict on an earlier version.
888
+ After rebuilding, `next` requests the new version's review; no stage or
889
+ human approval is supplied by answering feedback.
890
+
891
+ A rig version's provenance config, review package and accepted parts must identify the same build.
892
+
893
+ Every build shown to anyone is a `versions[]` entry, numbered in order across
894
+ both kinds:
895
+
896
+ | Field | Holds |
897
+ | --- | --- |
898
+ | `n` | the version number, `1, 2, …` |
899
+ | `original_n` | on pre-interview versions only, the unchanged source id as a number, string or `null`; non-null ids are unique |
900
+ | `config_lost` | on pre-interview versions only, `{ note, source }` with nonempty strings when the build config did not survive; cannot accompany `provenance.config` or authorize post work |
901
+ | `outputs_lost` | on pre-interview versions only, `{ note, source, recorded? }` with nonempty strings and missing `recorded: [{ path, sha256 }]` bindings disjoint from surviving `outputs` (or `{ note, source }` beside `outputs: []`) when frozen outputs did not survive; cannot authorize post work |
902
+ | `kind` | `rig` or `post` |
903
+ | `rig_version` | for a post version, the `n` of the accepted rig it is built on (accepted before the post version's `at`); `null` for a rig version |
904
+ | `at`, `built_by`, `built_with` | when, which agent or tool, and the package versions used (`{ "@rig-c/parts": "…", "@rig-c/core": "…" }`) |
905
+ | `review_origin` | null for a build made without review feedback, otherwise the applied-round record below |
906
+ | `answers_feedback` | the verdicts this version answers: `[{ "version", "verdict" }]` (version number and index into its `verdicts[]`); empty for a first version |
907
+ | `changed` | what changed from the previous version of the same kind, in a sentence |
908
+ | `outputs` | `outputs/v<N>/` files with sha256 and size, frozen when shown |
909
+ | `evidence` | `evidence/v<N>/` files with sha256 and what each shows; on a rig version accepted at `rig-review`, one is the still-frame record (`kind: "still"`, `camera: "fixed"`, `frame`) |
910
+ | `provenance` | the config this version was built from and, per section, its citations |
911
+ | `verdicts` | what people said: `by`, `at`, `review`, `verdict` (`accepted`, `bounced`, `provisional`), `verbatim`, `source`, `on_items[]` |
912
+
913
+ A provenance citation may add `asks: ["<ask id>", …]`, naming the
914
+ canonical asks its guide records in `answer.asks`. For example,
915
+ `{ "entry": "torso", "guide": "meshes", "asks": ["bend"], "quote": "The coat is one piece" }`
916
+ cites the user-answered bend ask. A delegated density citation uses
917
+ `{ "entry": "density", "guide": "meshes", "asks": ["density"], "delegated": true }`
918
+ and needs that answer's decided `agent_decision`. Each named ask's coverage
919
+ and supporting quote or decision must come from the same answer, including
920
+ answers preserved in history. A user ask requires a quote and cannot be
921
+ claimed as delegated. Missing ids on a guide with per-ask coverage warn.
922
+
923
+ A verdict item with `answer_was: "delegated"` may add `ask: "<ask id>"`.
924
+ The ask must be recorded as delegated in a current or historical answer.
925
+ `rig-create review … --item meshes:delegated --ask meshes=density` writes
926
+ that field. `--ask` is optional, accepts one ask per named item, and is for
927
+ delegated feedback. Missing it on a guide with per-ask coverage warns.
928
+ Guides without per-ask coverage gain no finding when these fields are absent.
929
+
930
+ `provisional` is a verdict given before the person has seen everything they
931
+ want to see — it holds a place and is replaced, never a basis for moving a
932
+ stage.
933
+
934
+ ### Registering a frozen rig build
935
+
936
+ Use `rig-version` to bring separately located frozen producer outputs into one
937
+ numbered version. This is a registration operation. Build, registration and
938
+ acceptance have separate dates and writers.
939
+
940
+ ```sh
941
+ rig-create rig-version 3 . \
942
+ --parts frozen/assembly/rig --rig frozen/out/rig --build frozen/out/build \
943
+ --idle frozen/render --config frozen/config.json \
944
+ --path-adapter frozen/path-adapter.json --compile-report frozen/build-report.json \
945
+ --toolchain-lock frozen/package-lock.json \
946
+ --at-source frozen/dated-record.json --at-pointer /finished_at --by agent \
947
+ --citations frozen/section-citations.json
948
+ ```
949
+
950
+ Source arguments resolve against the project directory. Absolute arguments may
951
+ name external frozen inputs. The writer retains copies under
952
+ `evidence/v<N>/registration/` and binds their exact bytes by SHA-256. The project
953
+ record refers to those retained paths, including the lock and cited date record.
954
+
955
+ `built_with` comes from the declared npm v3 lock's
956
+ `packages["node_modules/@rig-c/parts"].version` and corresponding core entry.
957
+ `registration.toolchain` is `{kind:"npm-lock-v3",source:{path,sha256,bytes}}`.
958
+ The `--toolchain-lock` argument explicitly declares the run-to-toolchain
959
+ association. This declaration establishes which frozen lock the registrant
960
+ selected. Producer-owned run attestation, including versions, start/finish and
961
+ output hashes, is tracked in parts#1165 and core#1718. A future source kind can
962
+ be added when that producer interface lands. Current readers support
963
+ `npm-lock-v3` only. Wrapper receipts and toolchain summary files supply no
964
+ registration contract.
965
+
966
+ Repeatable `--run-record <file>` binds at most one producer `run.json` per
967
+ spec beside `--toolchain-lock`, recorded in the order given as
968
+ `registration.runs: [{spec,source:{path,sha256,bytes}}]`. The dispatcher in
969
+ `src/run-record.ts` accepts `rig-run/1` (`@rig-c/parts` `rig` or `assemble`) and
970
+ `rigc-run/1` (`@rig-c/core` or its `rig-c` alias, `build`, with required `rig` and `motion` input roles and
971
+ optional `manifest` and `cuts` sorted in that role order and UTF-16 code-unit
972
+ path order), validating only the published keys, key order and types without
973
+ re-running or recomputing producer measurements. `interview --check` re-reads
974
+ each bound record and refuses a byte/SHA-256 mismatch
975
+ (`RUN_RECORD_HASH_MISMATCH`), an unknown spec (`RUN_RECORD_SPEC_UNKNOWN`), a
976
+ missing or malformed input role (`RUN_RECORD_ROLE_INVALID`) or a malformed
977
+ shape (`RUN_RECORD_MALFORMED`). Lock-only `frozen-rig/1` versions without
978
+ `registration.runs` WARN before `RUN_RECORD_SINCE = 2026-11-08T00:00:00Z` (by
979
+ `registration.at`) and refuse from `RUN_RECORD_SINCE` onward under
980
+ `RUN_RECORD_REQUIRED`.
981
+
982
+ `at_source` is `{path,sha256,bytes,pointer}`. The nonempty RFC 6901 JSON pointer
983
+ selects an explicit UTC ISO timestamp ending `Z` or `+00:00`, including fractional
984
+ seconds. `at` preserves that exact string and `at_basis` is `"not-after"`.
985
+ The registrant declares that the build had finished by that cited instant.
986
+ The clock supplies `registration.at`. A cited bound at or before a reopen
987
+ covering `rig-review`, as of registration, refuses the write. A later bound is
988
+ admitted with its retained citation for review. A reopen after registration
989
+ preserves the historical record and the existing liveness rules supersede it.
990
+ Acceptance dates remain on verdicts written through the review and signoff flow.
991
+
992
+ The copied rig, motion and model use producer specs `rigc-rig/1`,
993
+ `rigc-motion/1` and `rigc-compiled/3`. The producer's compile document must
994
+ be a successful `build-report/1`. Idle
995
+ evidence is a public `rigc render` directory with `frames.json`, spec
996
+ `rigc-frames/1`, and an `idle` set. The writer binds that manifest and every
997
+ copied frame and diagnostic file. It preserves the producer's fps, viewport,
998
+ skin and preroll declarations, including optional-field absence. A directory
999
+ containing only images and a wrapper receipt needs a public producer re-render.
1000
+
1001
+ The copied output layout is `outputs/v<N>/out/parts.json`, `out/parts/`,
1002
+ `out/rig/`, `out/check/build/` and `outputs/v<N>/idle/`. Every copied output
1003
+ gets its actual `path`, `sha256` and `bytes` in `versions[].outputs`.
1004
+ `registration.copied` records file and byte totals. Sources are rechecked after
1005
+ copying, and the copy is byte-verified before registration. The normal
1006
+ `review-package` materializer then creates `derived/review/v<N>/`, using the
1007
+ same accepted-parts and config identity checks as its standalone command.
1008
+
1009
+ The retained original config and adapter are bound as `registration.config_source`
1010
+ and `registration.path_adapter`. Registrations using pointer adaptation declare
1011
+ `registration.path_adapter.format` as `json-pointer/1`. Removing an adapter row
1012
+ keeps that contract active. These registrations also bind the original
1013
+ and runtime requirements as `registration.requirements_source` and
1014
+ `registration.requirements`. Each absolute config value requires an exact
1015
+ adapter row `{at:".section.field",before,after}` matching its runtime `after`
1016
+ value, and relative `config` or `requirements` asset references use explicit
1017
+ `{doc:"config"|"requirements",pointer,before,after}` rows (recorded in the
1018
+ retained `path-adapter.json`). The writer retains the original `config` and
1019
+ `requirements` byte-identical, copies every referenced asset (`assemble.take`,
1020
+ `assemble.editor_receipts`, `assemble.cuts`, `limbs`, `depth`,
1021
+ `setup_painting.figure_mask` and `setup_painting.backdrop`) to literal
1022
+ in-package paths (`no ..`, `no :`), verifies declared `sha256` digests before
1023
+ copying, and checks that the runtime `config.json` and `requirements.json`
1024
+ equal the retained sources after applying only the declared `path_adapter`
1025
+ substitutions. Registrations without pointer adaptation retain their original
1026
+ implicit take and editor-receipt relocation, permit unused legacy adapter rows
1027
+ and do not require source-only relative assets or a backdrop that their writer
1028
+ did not retain. Missing mappings, unlisted runtime differences, escaping
1029
+ references, symlinks, hash mismatches and incompatible file or directory
1030
+ collisions refuse. Identical file bytes may share a target, including an
1031
+ explicit image inside a named plate directory. Producer
1032
+ outputs retain their original bytes. Packaging relocates the config's and
1033
+ requirements' named inputs without rerunning producer measurements.
1034
+
1035
+ Optional `--citations` reads an agent-authored map of the existing
1036
+ `provenance.sections` shape. The writer retains that map and binds the adapted
1037
+ config under `provenance.config`. Omission records an empty section map. The
1038
+ writer runs the full check on the candidate record and refuses any refusal it
1039
+ adds, so every applicable config section needs its citation at registration.
1040
+
1041
+ `registration.kind` is `"frozen-rig/1"`. The registration validator is shared
1042
+ with `interview --check` and opts in only versions carrying `registration`.
1043
+ Existing versions keep their contract, so this additive writer adds no SINCE
1044
+ constant. When `n > maxRegistered + 1`, every skipped number `K` between the
1045
+ previous highest registered version and `n` must already have at least one
1046
+ unregistered destination directory (`outputs/v<K>`,
1047
+ `evidence/v<K>/registration` or `derived/review/v<K>`) present on disk at
1048
+ registration (re-verified before rename); the writer records the admission
1049
+ receipt `registration.skipped` as `[{n:K,destinations}]`, and `interview --check`
1050
+ validates that receipt (`prevMax + 1 .. n - 1`, unregistered numbers and
1051
+ nonempty canonical destination paths) without re-statting historical directories
1052
+ after archival or in retained closures. Unexplained empty gaps, occupied
1053
+ destinations on `n` itself, registered numbers in `skipped` and non-directory or
1054
+ symlinked destination paths at registration refuse. Versions that advance by one
1055
+ (`n === maxRegistered + 1`) omit `registration.skipped`. The writer validates
1056
+ its registration and normal
1057
+ package identity,
1058
+ checks that project bytes are unchanged during copying, then atomically records
1059
+ the new version. A refused write preserves project bytes and removes its newly
1060
+ created outputs, retained inputs and review package. Retry uses the same next
1061
+ number after a refusal. A successful version remains frozen.
1062
+
1063
+ Registration leaves stage, signoffs and verdicts intact. The human review uses
1064
+ the emitted review package, existing review/signoff writers record the result,
1065
+ and `advance` performs the authorized forward move. The synthetic public-render
1066
+ fixture and its producer commands are documented in
1067
+ [tests/fixtures/rig-registration](../tests/fixtures/rig-registration/README.md).
1068
+
1069
+ ### Migrating work done before the interview
1070
+
1071
+ Keep `kind: "rig"` or `"post"` and add `pre_interview: true`. The flag records
1072
+ when the work was made while preserving which review judges it and which rig
1073
+ post work rests on. These versions need no `provenance`. Create-built versions
1074
+ continue to cite their interview and retain the user's words in verdicts.
1075
+
1076
+ A pre-interview verdict has create's `by`, valid `at`, `review`, `verdict`,
1077
+ `source` and `on_items[]`. When the words survive, keep them in `verbatim`.
1078
+ When only someone's record of the user survives, set `verbatim: null`, state
1079
+ that it is a record in `note`, put the record there and name its `source`.
1080
+ The check requires the note and source. A summary never becomes an interview
1081
+ quotation. Historical `not-asked` items remain in the verdict without invented
1082
+ `additions[]` in today's guide. Current answers and sign-offs are still checked.
1083
+
1084
+ Keep the complete original verdict array unchanged in `original_verdicts` on
1085
+ the version. Its entries occupy the same indices in `verdicts[]`, each copied
1086
+ unchanged into `as_recorded` beside the converted fields. The check compares
1087
+ all fields and the array order, including repeated verdicts. Further reviews
1088
+ may be appended without `as_recorded`. The original `verdict` string maps by
1089
+ this exact table, also exported as `PRE_INTERVIEW_VERDICTS`.
1090
+
1091
+ | Original `verdict` | Create `verdict` |
1092
+ | --- | --- |
1093
+ | `accepted`, `Accepted` | `accepted` |
1094
+ | `accepted (sent to owner)`, `Accepted (sent to owner)` | `accepted` |
1095
+ | `bounced`, `Bounced` | `bounced` |
1096
+ | `provisional`, `Provisional` | `provisional` |
1097
+ | `accepted (HQ-provisional, sent to owner)` | `provisional` — the reviewer marked “HQ-provisional”, org HQ ruling 2026-11-06 |
1098
+ | `accepted-provisional` | `provisional` — the reviewer marked “provisional”, org HQ ruling 2026-11-06 |
1099
+ | `ok for now, polish wanted` | `accepted`, polish in `on_items[]` — owner 2026-11-03, “`<pilot>`도 여기까지 OK” and “일단 여기까지는 OK”; org HQ ruling 2026-11-06 |
1100
+ | `close, polish wanted` | `provisional`, polish as its open item — owner 2026-11-03, “`<pilot>` 많이 좋아졌다. 조금만 더 다듬으면 되겠어”; org HQ ruling 2026-11-06 |
1101
+
1102
+ An unlisted string is refused as `PRE_INTERVIEW_VERDICT_UNKNOWN`. Preserve it
1103
+ and obtain an explicit mapping before extending the table. A converted value
1104
+ that disagrees with the table is `PRE_INTERVIEW_VERDICT_MISMATCH`.
1105
+
1106
+ Every historical version lists its frozen `outputs[]` with project-relative
1107
+ `path`, `sha256` and `bytes`. The check reads every file and compares its hash
1108
+ and size. If frozen outputs did not survive, declare
1109
+ `outputs_lost: { note, source, recorded }` via
1110
+ `rig-create version outputs-lost <n> --note <text> --source <text>`, keeping any
1111
+ outputs still readable with their recorded sha256 in `outputs` (`outputs: []` when
1112
+ none survived) and moving the missing ones into `recorded: [{ path, sha256 }]`
1113
+ alongside what was lost and the source record that says so. Both
1114
+ `note` and `source` must be nonempty strings. Earlier `{ note, source }`
1115
+ declarations beside `outputs: []` without `recorded` remain valid, so no contract
1116
+ date is added. A loss declaration without `recorded` beside nonempty `outputs`,
1117
+ a `recorded` entry overlapping `outputs` or readable on disk, or `outputs_lost`
1118
+ on a create-built version is refused (`PRE_INTERVIEW_OUTPUTS_LOST_MALFORMED`). If
1119
+ no output is lost (every recorded output is readable on disk with its sha256),
1120
+ the writer refuses (`OUTPUTS_LOST_HAS_OUTPUTS`) and names the readable outputs.
1121
+ Handoff refuses a selected rig with `outputs_lost`
1122
+ as `PRE_INTERVIEW_OUTPUTS_LOST`, and post records cannot use it as their rig
1123
+ basis (`POST_ON_UNACCEPTED_RIG`). All
1124
+ pre-interview versions precede the first create-built version, with positive,
1125
+ strictly increasing `n` across the combined history.
1126
+
1127
+ For a lost build config, first recover the producer version from that version's
1128
+ own source record and put it in `built_with["@rig-c/parts"]`. A version's
1129
+ producer is recorded from its own source record, never inferred from neighbours.
1130
+ A producer older than 2.4.0 receives `CG_GATES_PREDATE` on a CG rig.
1131
+ If the config did not survive, declare `config_lost: { note, source }` with
1132
+ nonempty strings explaining the loss and naming its source. This requires
1133
+ `pre_interview: true` and absent or null `provenance.config`. A CG rig receives
1134
+ `CG_GATES_CONFIG_LOST` instead of `CG_GATES_UNREAD`, preserving the check exit
1135
+ when the rest of the record passes. A known old producer and a declared config
1136
+ loss receive both warnings. The declaration is also available when the source
1137
+ names no producer version. Handoff refuses a selected rig with this declaration
1138
+ as `PRE_INTERVIEW_CONFIG_LOST`; post records cannot use it as their rig basis.
1139
+ Build and accept a new rig with its retained config before post work.
1140
+
1141
+ An accepted pre-interview rig with surviving outputs and no config loss still
1142
+ authorizes post work. Its human
1143
+ `rig-review` verdict, valid acceptance time, still-frame record and
1144
+ `rig-review` sign-off remain required. `handoff` reports `pre_interview` in
1145
+ JSON and prints that status and the verdict's source in text. It also lists
1146
+ the note and source of historical output losses, and refuses a selected rig
1147
+ whose outputs were lost. A later rig
1148
+ requires its own acceptance and supersedes the older rig in the usual way.
1149
+ Other checks, including the CG gates where applicable, continue to run.
1150
+
1151
+ For an in-flight consumer project keeping history under `x-pre_create.versions`,
1152
+ perform the following migration on its own decision record.
1153
+
1154
+ 1. Preserve the original array and frozen outputs before editing. For each
1155
+ version, recover its complete original verdict array from the existing
1156
+ `as_recorded` entries or the source record. Keep that array in
1157
+ `original_verdicts` and retain each full entry as `as_recorded` at the same
1158
+ index. Do not replace a missing original with a reconstructed quote.
1159
+ 2. Move the historical entries into `versions[]`, keeping `kind` and adding
1160
+ `pre_interview: true`. Put them before the create-built entries, preserve
1161
+ a copy of the source id in `original_n` (number, string or `null`). Assign
1162
+ positive, strictly increasing `n` in record order. Non-null `original_n`
1163
+ values must be unique; multiple nulls are allowed. Rewrite `answers_feedback`
1164
+ version references and post `rig_version` references to the new `n`.
1165
+ Create-built versions carry no `original_n`.
1166
+ Every migrated rig version must retain a readable `at` from its build record;
1167
+ if no date can be recovered, stop the migration rather than inventing one
1168
+ (`RIG_VERSION_AT_INVALID`).
1169
+ Remove the moved `x-pre_create.versions` array. A nonempty array left there
1170
+ produces the `PRE_INTERVIEW_UNMIGRATED` warning while migration is pending.
1171
+ It preserves the existing check exit code. Migrated records are checked
1172
+ with the refusals described above.
1173
+ 3. Fill create-shaped verdict fields from known records, using the mapping
1174
+ table, stating their source and recording summaries as `verbatim: null`
1175
+ with `note`. Preserve carried `on_items[]` and map them to the interview's
1176
+ item ids. Recover dates from dated evidence without inventing them.
1177
+ 4. First record any producer version named by each version's own source in
1178
+ `built_with`; never infer it from neighbouring versions. Restore the build
1179
+ config and its provenance binding if it survived. Otherwise declare
1180
+ `config_lost: { note, source }` explaining the loss and its source; this
1181
+ version cannot authorize post work. `rig-create version config-lost <n> --note <text> --source <text>`
1182
+ writes this declaration.
1183
+ Restore each frozen output and record its actual `path`, `sha256` and
1184
+ `bytes`. When some or all frozen outputs did not survive, declare `outputs_lost`
1185
+ with the known loss in `note` and its record in `source`;
1186
+ `rig-create version outputs-lost <n> --note <text> --source <text>` keeps
1187
+ any readable outputs in `outputs` (`outputs: []` when none survived) and
1188
+ moves the missing `{ path, sha256 }` bindings into
1189
+ `outputs_lost.recorded`. Check that all
1190
+ `answers_feedback` and `rig_version` references use the new `n`, including
1191
+ references from create-built entries. Retain acceptance stills and genuine
1192
+ stage sign-offs. The flag
1193
+ supplies no missing review evidence or sign-off.
1194
+ 5. On the first create-built version, list the earlier feedback it answers in
1195
+ `answers_feedback`, including carried items where relevant. The existing
1196
+ `review --answers-feedback <version>:<verdict-index>` writer can append
1197
+ these references. Keep that version's provenance and interview words.
1198
+ 6. Run `rig-create interview --check <project dir> --json`, resolve the named
1199
+ refusals and inspect `rig-create handoff <project dir> --json` before post
1200
+ work. The synthetic [migration fixture](../tests/data/pre-interview.json)
1201
+ contains one bounced rig and one accepted rig carrying a timing item.
1202
+
1203
+ ## The record, field by field
1204
+
1205
+ Top-level fields of `project.json`:
1206
+
1207
+ | Field | Holds |
1208
+ | --- | --- |
1209
+ | `schema` | `rig-create/project@0` |
1210
+ | `name` | the folder name; a short identifier |
1211
+ | `title` | the human title of the painting or rig |
1212
+ | `stage`, `stage_values` | the current stage and the list it comes from |
1213
+ | `signoffs[]` | every human signature: `stage`, `checkpoint` (step id or `null`), `by`, `at`, `verbatim`; plan signatures add `steps[]` naming covered steps and `step_hashes` binding their approval fields. `by` names whose decision is recorded, including their delegated instruction written by an agent, and `by: "agent"` remains refused (`SIGNOFF_NOT_HUMAN`). |
1214
+ | `interview` | `question_set` (the prompt set and package version the waypoints were read from), `held`, `date`, `interviewer`, `respondent`, `framing[]` (`id`, `text`, `source`, `required_for`, `answer`, `history[]`), `waypoints[]` (`id`, `config[]`, `prompts[]` of `id`/`text`/`source`/`required_for`, `guide`, `history[]`), `post` (`held`, `date`, `items[]` shaped like waypoints with `guide`) — answer shapes in [INTERVIEW.md](INTERVIEW.md) |
1215
+ | `plan` | `drafted_by`, `drafted_at`, current unique `steps[]`, optional append-only `replaced[]` — above |
1216
+ | `plan.replaced[]` | full previous `step`, `sha256` from `stepHash(step)`, `action` (`revised` or `removed`), ISO `at`, drafter `by`, identified by `step.id` plus `at`, in nondecreasing date and write order |
1217
+ | `split` | the `assemble` checkpoint's data: `label_values`, `proposal_image`, `proposed_by`, `proposed_at`, `parts[]` (`name`, `label`, `note`) |
1218
+ | `inputs[]` | `path`, `sha256`, `bytes`, `origin` (where the file came from, in words), `at` (writer timestamp) |
1219
+ | `derived[]` | `path`, `what`, `command`, `inputs[]` (`path`, `sha256`), `bytes` |
1220
+ | `versions[]` | above |
1221
+ | `archive` | `null`, or `{ "where", "at", "by" }` if the finals were moved out of the project |
1222
+ | `collection_binding` | optional on collection members: `{ "path", "sha256", "spec": "rig-create/collection-binding@1", "collection_sha256" }` binding a project-relative snapshot of `../../collection.json` so retained checks and model-free record publication run without reading outside the member root |
1223
+ | `notes[]` | free text that is not a decision; anything that is one goes in the field it belongs to |
1224
+
1225
+ ## How an agent resumes a project
1226
+
1227
+ 1. Read `project.json` — nothing else first. `stage`, the last `signoffs[]`
1228
+ entry, the first `plan.steps[]` entry not `done` and the last `versions[]`
1229
+ entry say where it stands. For a collection member, read the collection's
1230
+ `collection.json` next ([COLLECTIONS.md](COLLECTIONS.md)): its stage and
1231
+ `plan.order` say whether this member may run now, and its interview holds
1232
+ every answer marked `inherited`.
1233
+ 2. If the interview has a `null` answer or the plan is unsigned, that is the
1234
+ next work, and it is the user's to answer; do not fill it from the image.
1235
+ 3. If the last version was bounced, its verdict's `verbatim`, its `on_items`
1236
+ and the files in its `evidence` are the brief. Open the evidence; do not
1237
+ reconstruct the reason from chat. Feedback with `answer_was: "not-asked"`
1238
+ becomes a guide sentence at its waypoint before the next version is built.
1239
+ 4. Check `inputs[]` hashes against `inputs/` before using them.
1240
+ 5. Reuse `derived/` only through `derived[]`: an entry whose input hashes no
1241
+ longer match is stale — regenerate it with its recorded command.
1242
+ 6. When a new version is shown to anyone: freeze `outputs/v<N>/`, hash it, add
1243
+ the `versions[]` entry and its evidence in the same turn.
1244
+
1245
+ ### Registering immutable inputs
1246
+
1247
+ ```sh
1248
+ rig-create input add inputs/source.png [<project dir>] --origin "synthetic source exported by the painting tool" [--painting [--projects-root <dir>] [--same-artwork-reason <file>]] [--json]
1249
+ ```
1250
+
1251
+ Run from the project, or pass its directory after the source path. The writer
1252
+ appends `{path, sha256, bytes, origin, at}` using the file's raw bytes and the
1253
+ current UTC timestamp. `--json` prints that record. It refuses an escaping or
1254
+ missing source, an already registered path, and changed bytes under a
1255
+ registered name. Add `--painting` to declare this registered input's path as
1256
+ `project.painting`. Each project has one declaration. A second declaration
1257
+ refuses with `INPUT_PAINTING_EXISTS`, preserving the original painting and reason.
1258
+ Only `init --painting` and `input add --painting` compare the declared file
1259
+ against sibling inputs. They refuse with `INIT_ARTWORK_EXISTS` when a
1260
+ `project.json` directly under `--projects-root` (default the project's parent
1261
+ folder) has an input with the same sha256, unless `--same-artwork-reason <file>`
1262
+ is given. This records `same_artwork: { reason, at, projects }`, preserving any
1263
+ previously recorded reason. Both artwork options require `--painting`.
1264
+ Plain `input add` accepts identical inputs across sibling projects. Older
1265
+ records may omit `painting`. Position, name and file type never declare it.
1266
+ The shared checker refuses `INPUT_PAINTING_INVALID` when a declaration is not
1267
+ the path of a registered input. Restart validates and preserves the declaration.
1268
+ A changed source needs a new file with a new name. A refused write leaves
1269
+ `project.json` byte-identical. The shared checker checks complete records against their retained files. Incomplete
1270
+ writer metadata is `INPUT_WRITER_MISSING`, dated by `inputs[].at` from
1271
+ `INPUT_WRITER_SINCE`. Earlier or unreadably dated entries WARN and retain their
1272
+ original history.
1273
+
1274
+ ## Prepared inputs and agent-found bounces
1275
+
1276
+ `rig-create derived add [<project dir>] --source inputs/source.svg --file derived/prepared.svg --operation "external scale and white composite"`
1277
+ records work already performed by another tool. It appends one `derived[]`
1278
+ entry; it transforms no pixels and changes no sign-off or stage. `derived add`
1279
+ is used instead of `prepare` because the operation has already run elsewhere.
1280
+ Both files must exist, have distinct project-relative paths, and be readable.
1281
+ Neither may be `project.json`: appending the record would change its own hash.
1282
+ The command measures both sha256s before writing anything:
1283
+
1284
+ ```json
1285
+ {
1286
+ "kind": "prepared-input",
1287
+ "source": { "path": "inputs/source.svg", "sha256": "<64 hex digits>" },
1288
+ "path": "derived/prepared.svg",
1289
+ "sha256": "<64 hex digits>",
1290
+ "operation": "external scale and white composite"
1291
+ }
1292
+ ```
1293
+
1294
+ `interview --check` requires the source and verifies its hash. A missing source
1295
+ or changed bytes in either present file are refused by name. A missing result
1296
+ warns with its file name and recorded operation, so a fresh clone passes while
1297
+ regenerable intermediates are absent. Only entries tagged `kind: "prepared-input"` use this contract; older
1298
+ untagged intermediates and projects with no `derived[]` entries keep passing.
1299
+ Restore derived files with the recorded operation when needed. The recorded
1300
+ hashes describe the bytes that must return.
1301
+ For a producer plate, supply `--source` for both its painting and uploaded mask,
1302
+ `--file` for its output, and `--receipt` for its `rig-parts-plate-receipt/1` JSON.
1303
+ The optional `receipt` records `{ path, sha256, spec }` from raw bytes. Its file
1304
+ must stay inside the project with no symlink components. Receipt paths resolve
1305
+ relative to the receipt directory. The checker compares declared paths and hashes
1306
+ to the derived sources and output, without repeating producer measurements.
1307
+ A retained scene registration identifies a derived plate. Its earliest scene
1308
+ `at` dates missing-receipt warnings or refusals under `PLATE_RECEIPT_SINCE`.
1309
+
1310
+ The filled example ships synthetic SVG bytes under
1311
+ [example-prepared](../templates/example-prepared/); copy `source.svg` to
1312
+ `inputs/preparation-source.svg` and `prepared.svg` to `derived/prepared.svg`
1313
+ when materializing its prepared-input record. These fixtures demonstrate
1314
+ recording, not a claim that the tool performed or verified pixel processing.
1315
+
1316
+ Busy-ground figure masks have a coverage gap: foreground subtraction excludes
1317
+ character pixels under that plane, so `SETUP_MATCHES_PAINTING` can pass with
1318
+ parts missing there. Keep the [default recipe](CHECKS.md#busy-ground-figure-masks).
1319
+ A character-plate matte can include inpaint fill as figure;
1320
+ [#113](https://github.com/rig-c/create/issues/113) tracks that upstream plate-making
1321
+ problem.
1322
+
1323
+ An agent rejecting its own route mid-step records it in that guide's
1324
+ `history[]`, separately from human `versions[].verdicts[]`:
1325
+
1326
+ ```json
1327
+ {
1328
+ "at": "2026-11-11T08:00:00Z",
1329
+ "bounce": {
1330
+ "cause": "agent",
1331
+ "failed": "The first split merged the eyelids into the face",
1332
+ "bar": "Blink needs the eyelids as their own layers",
1333
+ "replacement": "Run a head pass as well as the full pass"
1334
+ }
1335
+ }
1336
+ ```
1337
+
1338
+ `failed`, `bar`, and `replacement` are non-empty text: what failed, against
1339
+ which bar, and what replaced it. This record does not grant a new user choice
1340
+ or sign-off and does not create a human review verdict. The check reads local
1341
+ histories and the collection histories an inherited answer cites. Compatibility
1342
+ is per history entry's own `at`, not the project's age: malformed bounces before
1343
+ `AGENT_BOUNCE_SINCE` (`2026-11-05T00:00:00Z`), or with no readable date, warn
1344
+ with the new shape. Malformed bounces on or after it are refused.
1345
+
1346
+ ### The package handed to rig review
1347
+
1348
+ Before showing a create-built rig version, run from the project directory:
1349
+
1350
+ ```sh
1351
+ rig-create review-package 1 --config evidence/v1/config.json --from outputs/v1/out
1352
+ ```
1353
+
1354
+ The source paths are explicit, project-relative inputs. The command copies the
1355
+ config unchanged and the build's `parts.json`, `parts/`, `rig/`, and
1356
+ `check/build/` into `derived/review/v1/`, with the config at `config.json` and
1357
+ the build files under `out/`. Every config-relative file named by the installed loader is copied at its relative path and included in the frozen inventory, including nested requirements, animations and a file-based control skeleton. The source build must include `rig/rig.json`,
1358
+ `rig/motion.json`, `rig/mesh_report.json` and
1359
+ `check/build/skeleton.model.json`. Copies preserve the package when the build
1360
+ directory is cleaned. The packager also retains `setup_painting.figure_mask.path` from the frozen requirements at its requirements-relative path, verifies its declared SHA-256, and records its package binding. Each copied file is hashed. The command writes no new
1361
+ build and moves no stage.
1362
+
1363
+ A config's named files are bound build inputs in versions and drafts, with draft inputs recorded separately as optional `config_inputs` path/raw-sha256 bindings while `files` remains outputs. A draft's named input binds at its project path wherever it sits inside the project, and is not copied into `derived/drafts/<id>`; only the draft's config, parts and outputs live under that root. A named input outside the root is a project input, not a regenerable draft artifact, so its absence refuses rather than warning `DRAFT_RIG_UNAVAILABLE`. A named input that is a directory, such as a See-through occluder plate named by `assemble.occluders[i].plate`, binds its producer's `receipt.json` — as `{path: <dir>/receipt.json, sha256, directory: <dir>, spec}` in a draft's `config_inputs`, and by copying and hashing `<dir>/receipt.json` in `review-package` — where `spec` is the installed parts' `PLATE_RECEIPT_SPEC` and the receipt must declare it; the receipt binds the plate's unreferenced sibling images, which create does not copy, hash or re-verify (any sibling explicitly declared as its own config input, such as `assemble.occluders[i].image`, is copied and hashed as that independent input).
1364
+
1365
+ The version's `review_package` field records `config` (`path`, `sha256`),
1366
+ `from` (the declared build directory), `files` (sorted package-relative
1367
+ `{path, sha256}` entries, including the copied config), and `open`:
1368
+
1369
+ ```json
1370
+ {"open":"rig-review open 'derived/review/v1/out'"}
1371
+ ```
1372
+
1373
+ Run that open command from the project directory. Review recognizes `out/`
1374
+ as a build root, opens its `check/` package and discovers the config beside
1375
+ the build root. The installed review package documents this discovery under
1376
+ *Producer handoff* in `docs/REVIEW_PACKAGE.md`. Its handoff hash identifies
1377
+ the exact copied config. The hash check binds the copies to this version's
1378
+ recorded input snapshot. The producer baseline in review determines whether
1379
+ those inputs reproduce the inspected rig.
1380
+
1381
+ `interview --check` warns with the package command when a rig version has no
1382
+ recorded package or when a recorded package is absent after a fresh clone.
1383
+ A present package must match the complete recorded file list and hashes.
1384
+ Regenerate a missing package from its recorded sources. If an existing
1385
+ package differs, the writer refuses replacement so the frozen copy can be
1386
+ inspected. A new build shown to the user gets a new version and package.
1387
+ Post versions go to direction review, outside this package contract.
1388
+ Pre-interview versions retain their existing history and output checks and
1389
+ are exempt from review-package warnings.
1390
+
1391
+ The filled example ships synthetic producer bytes under
1392
+ `templates/example-review/`. Its two `review_package` records are generated
1393
+ by `bun scripts/generate-example-review.ts <fresh scratch project>`. Add
1394
+ `--check` to verify that generation reproduces the shipped record bytes.
1395
+ The script runs the public `review-package` command for both rig versions.
1396
+ `reviewExampleFiles()` in `tests/review-fixture.ts` maps those bytes to the
1397
+ declared sources and package destinations, and both example fixture helpers
1398
+ materialize them. The regression test generates two fresh projects and
1399
+ compares every record and file byte before checking the filled example with
1400
+ zero refusals and zero warnings.
1401
+
1402
+ Apply results return in review feedback as `feedback[].context.applied`.
1403
+ Keep the JSON written by `rig-review feedback` in the project and record one
1404
+ applied round on the next version as `review_origin`:
1405
+
1406
+ ```json
1407
+ {
1408
+ "feedback": { "path": "evidence/review-feedback.json", "sha256": "<file SHA-256>" },
1409
+ "source_version": 1,
1410
+ "feedback_id": "<feedback item id>",
1411
+ "applied": { "schema": "rig-review-applied/1", "...": "complete context.applied object" }
1412
+ }
1413
+ ```
1414
+
1415
+ The containing version is the next-version link. `source_version` names the
1416
+ immediately preceding version of the same kind. Copy the selected item's
1417
+ complete non-null `context.applied` into `applied`, preserving every field.
1418
+ A build made without feedback records `review_origin: null`, including the
1419
+ filled example. This declaration is independent of human `answers_feedback`.
1420
+
1421
+ The check binds the feedback's bytes, selected item and applied result to the
1422
+ source package's config, handoff files and inspected compiled model. In the
1423
+ released folder adapter, `reviewedSource.rig` identifies that compiled model
1424
+ and `motion` is null; producer rig and motion hashes live in `handoff.files`.
1425
+ The retained feedback is a single-build `rig-review-session/1` export.
1426
+ Record the resulting version's outputs, hashes and provenance and write its
1427
+ review package as usual. This chain records the applied result and the next
1428
+ build's declared origin. Create does not execute the Apply or build.
1429
+
1430
+ A missing declaration on a version dated before
1431
+ `APPLIED_REVIEW_SINCE = 2026-11-06T00:00:00Z`, or without a readable date,
1432
+ warns to preserve in-flight history. From that boundary it is refused.
1433
+ Pre-interview versions retain their historical contract.
1434
+
1435
+ ## Per-ask guide coverage
1436
+
1437
+ Rig waypoint guides carry `asks: [{ "id": "tracks", "state": "user" },
1438
+ { "id": "extent", "state": "delegated" }, …]`, naming each applicable ask
1439
+ in `src/waypoints.ts`. A user entry points to the guide's existing `verbatim`
1440
+ and `value`; it is a recorded interpretation, not automatic proof that prose
1441
+ answers a question. A delegated entry records an explicit hand-off in those
1442
+ words and uses the guide's `agent_decision`. Its free-text `covers` still names
1443
+ the delegated remainder; its `value` and `reasoning` are written before approval.
1444
+ Whole-guide delegations list every ask as delegated. Not-applicable waypoints
1445
+ need no coverage. `states` and `transitions` apply only to `fullscreen.game_logic`; `tracks`
1446
+ and `blink` apply to other rig types. Post items are outside this contract.
1447
+
1448
+ The independent `ASK_COVERAGE_SINCE = 2026-11-05T00:00:00Z` boundary reads the
1449
+ resolved guide's own `at`, including inherited guides. An older or undated guide
1450
+ with no `asks` is warned about, preserving its exit code. An explicitly recorded
1451
+ list is checked regardless of age. Missing, unknown, duplicate or invalid entries
1452
+ must be fixed; free-text `agent_decision.covers` alone is not per-ask coverage.
1453
+
1454
+
1455
+ ## Explicit adoption of the split interview
1456
+
1457
+ `interview.order: { "spec": "rig-create/stage-order@1", "adopted_at": "<clock ISO date>" }`
1458
+ is the only order switch. A date does not establish which contract was used.
1459
+ `init` writes this metadata. An old record keeps its old checks and gets one
1460
+ `STAGE_ORDER_LEGACY` warning: its old rig guides are historical answers, never
1461
+ certification that they were asked after parts. No signature, checkpoint hash
1462
+ or old version is rewritten. To adopt, preserve the old rig guides whole in
1463
+ that question's `history[]`, ask fresh answers against accepted parts, re-sign
1464
+ `interview` after adopting to approve the changed interview, and record the first
1465
+ human `signoff rigging-interview`. That writer records adoption
1466
+ and the live parts/structure context. Stage movement remains a separate human
1467
+ act, as for existing forward sign-offs. The filled legacy example is frozen in
1468
+ `templates/legacy-example-project.json`. Read under the current 2.16.0 pin it warns with that order warning plus
1469
+ `QUESTION_SET_STALE`, because its frozen question set remains at 2.14.1, and it is refused by the build-identity gate
1470
+ (`REVIEW_PACKAGE_CONFIG_MISMATCH` and `REVIEW_PACKAGE_PARTS_MISMATCH` on v1 and v2, plus
1471
+ `REVIEW_PACKAGE_PART_FILE_MISMATCH` for each of three approved part images missing per version), because its versions, dated after
1472
+ `BUILD_IDENTITY_SINCE`, claim configs and parts their review packages do not carry. Its rig-review and direction-review signatures also postdate `SIGNOFF_TARGET_SINCE` and lack targets, so each refuses `SIGNOFF_TARGET_MISSING`. Its interview, two plan-approved and post-interview signatures also lack answer bindings and each refuses `SIGNOFF_ANSWERS_MISSING`. Its bytes stay frozen.
1473
+ The new `example-project.json` is a synthetic new-order narrative at 0/0.
1474
+
1475
+ Framing and source (`seethrough`, `assemble`) are the first interview. A
1476
+ `plan-approved` signature covers only those parts steps in new-order records.
1477
+ The later `rigging-interview` covers `bones`, `meshes`, `constraints`, `motion`.
1478
+ Keep `phase: "rig"`; approval is resolved by step, not phase alone. Legacy
1479
+ `plan-approved` retains its old all-rig-step coverage. Post approval is unchanged.
1480
+ `plan.drafted_at` dates the parts plan; `plan.rigging_drafted_at` dates the later
1481
+ rigging plan, so drafting it does not invalidate the earlier parts signature.
1482
+ Each plan signature hashes only its covered steps. `--steps` cannot cross
1483
+ approval ownership. Pending later steps/answers may remain unfilled before their
1484
+ interview; production execution still requires its proper human approval.
1485
+
1486
+ The hand pass is the correction loop of `rig-review`, including rig and
1487
+ animation inspection, described corrections and direct edits. It adds no stage
1488
+ or acceptance authority. Existing `review_origin`/applied-review chains retain
1489
+ producer feedback; `review_origin: null` remains legal for a first rig. A
1490
+ separate choice-answer schema and correction-path union are subsequent cards.
1491
+
1492
+ ## Labelled draft rigs and structure decisions
1493
+
1494
+ After assemble, `parts`/`parts-review` may use a proposed draft to inspect meshes
1495
+ and refine chest structure before asking the production rigging plan. The
1496
+ draft's proposed bones and weights are not the person's rigging answers.
1497
+
1498
+ ```sh
1499
+ rig-create draft-rig <id> [<dir>] --config <path> --from <output dir> [--parts <dir>] [--json]
1500
+ rig-create structure-snapshot [<dir>] --draft <id> --verdict <path> --out <path>
1501
+ rig-create signoff parts-review --by <who> --evidence <path> --diff-report <path> --component '<id>=<status>:<why>' --draft <id> --structure-file <path> --verbatim-file <file>
1502
+ ```
1503
+
1504
+ The recorder reads existing output; it runs no producer or review command.
1505
+ It appends `drafts[]` with `{ id, kind: "draft-rig", at, producer: {name,version},
1506
+ config: {path,sha256}, parts: {path,files:[{path,sha256}]}, files:[{path,sha256}] }`.
1507
+ `parts.files` binds `parts.json`, `parts/*` and the raster `parts.json` itself
1508
+ declares under `painting_in_rig`; an undeclared file is not bound, and a draft
1509
+ recorded before the declared raster was bound warns `DRAFT_DECLARED_FILE_UNBOUND`.
1510
+ `producer` is the `package` of the `rig-run/1` `run.json` parts writes beside
1511
+ `rig.json` (`<out>/run.json`, or `<root>/rig/run.json` in a build root), which
1512
+ `files` already binds; a malformed or other-spec stamp refuses under its
1513
+ `RUN_RECORD_*` code. Without one, `producer` is the installed `@rig-c/parts` and
1514
+ the check warns `DRAFT_PRODUCER_ASSUMED`; a recorded producer that disagrees with
1515
+ its bound stamp warns `DRAFT_PRODUCER_MISMATCH`.
1516
+ All files must resolve under `derived/drafts/<id>/`. Standalone `rig --out`
1517
+ requires explicit `--parts`; only an unambiguous build root containing
1518
+ `parts.json`, `parts/` and `rig/` supplies those inputs implicitly. Ambiguous
1519
+ layouts refuse, and new registrations require exactly one stack candidate at
1520
+ `D/recomposite_rig.png` or, when the recorded parts directory D is named `rig`,
1521
+ `D/../render/recomposite_rig.png`; missing or ambiguous candidates refuse with
1522
+ `DRAFT_RIG_STACK`, remedied by re-running `rig-parts build` into the draft output
1523
+ or keeping assemble's `render/` beside `rig/`. Symlinks out of the root and changes to present recorded input bytes refuse.
1524
+ Absent ignored draft files warn as `DRAFT_RIG_UNAVAILABLE`, including after a
1525
+ clone; they do not invalidate an accepted record. Present files still verify
1526
+ even when another file is absent.
1527
+ These are **create-owned artifact identities**. The config/output association
1528
+ is a declaration, not measured reproducibility or producer provenance. There
1529
+ is no retained producer handoff file here: review constructs its own live
1530
+ handoff when it opens the draft.
1531
+
1532
+ Drafts allocate no production version number. A declared draft, reserved draft
1533
+ path or registered draft artifact cannot be reviewed, packaged or handed off
1534
+ as a `versions[]` rig. A separately authorised production build gets its own
1535
+ output root and numbered version. This checks declared artifact identity;
1536
+ it does not claim to recognise untracked byte copies after their identity is
1537
+ stripped. Identical model hashes alone are not blacklisted.
1538
+
1539
+ `--structure-file` retains a declaration snapshot approved by the human parts
1540
+ sign-off, not a fabricated review export. `structure-snapshot` writes it:
1541
+
1542
+ ```json
1543
+ {
1544
+ "schema": "rig-create/structure-decisions@0",
1545
+ "draft_id": "sample",
1546
+ "config": { "path": "derived/drafts/sample/config.json", "sha256": "<config hash>" },
1547
+ "entries": [ { "path": "/meshes/torso", "value": { "contour": { "regions": [] } } } ]
1548
+ }
1549
+ ```
1550
+
1551
+ Each entry is the complete exact `meshes.<part>` declaration, including
1552
+ `contour.bust` or `contour.regions` where present. Paths use JSON Pointer
1553
+ escaping. Explicit `entries: []` is legal when no declarations are carried.
1554
+
1555
+ A production config may differ from this snapshot only when a live (not
1556
+ reopened) `rigging-interview` sign-off with `checkpoint: null` follows that
1557
+ parts-review sign-off, and `interview.waypoints[meshes].guide.at` is strictly
1558
+ after parts-review and no later than the rigging-interview sign-off. The
1559
+ interview approval must already exist at the version's build time. Otherwise,
1560
+ answer the rigging meshes guide, or renew parts-review.
1561
+
1562
+ `rig-version` computes `versions[n].provenance.structure_delta` from the
1563
+ retained snapshot and config: an ordered list of `{entry, snapshot, version}`,
1564
+ where `entry` is the snapshot JSON Pointer and the other fields are complete
1565
+ declarations (`version: null` means absent). It omits the field when unchanged.
1566
+ The check recomputes it and refuses `STRUCTURE_DELTA_MISMATCH` on disagreement.
1567
+ `interview --check` and `next` WARN `STRUCTURE_DELTA_REVIEW` with the entry count;
1568
+ the rig-review verdict and sign-off cover changed structure. No producer
1569
+ measurement or new review act is introduced.
1570
+
1571
+
1572
+ The parts verdict names no per-declaration approval: an approved verdict
1573
+ approves the reviewed draft. So `structure-snapshot` copies `draft_id` and the
1574
+ draft's own `config: {path, sha256}` binding, and writes one entry per key of
1575
+ that config's `meshes`, in the config's key order, each value copied exactly.
1576
+ A config without `meshes` gives `entries: []`. It computes no mesh. It reads
1577
+ `--verdict` with the parts-review sign-off's own reader, so a verdict that is
1578
+ not approved refuses `PARTS_VERDICT_NOT_APPROVED`, and a changes-requested
1579
+ review gives no snapshot. The verdict must also review that draft's parts: both
1580
+ writers and the check match its `source.files[]` against the draft's
1581
+ `parts.files[]` and `files[]` by sha256, never by path, leaving out only the
1582
+ declared `painting_in_rig` raster. Every verdict file must be one of the
1583
+ draft's, and every `parts.files[]` entry must be in the verdict; any other
1584
+ difference is refused as `PARTS_VERDICT_DRAFT_MISMATCH`. A parts view that
1585
+ shows meshes also lists `rig.json`, `mesh_report.json` and the mesh rasters,
1586
+ which the draft binds in `files[]`; an output the verdict does not list is not
1587
+ missing, since a parts-only view lists none. It refuses an unknown draft
1588
+ (`STRUCTURE_SNAPSHOT_DRAFT_UNKNOWN`), a draft config whose bytes changed since
1589
+ the draft was recorded (`STRUCTURE_SNAPSHOT_CONFIG_CHANGED`), and an `--out`
1590
+ that is not a literal project path (`STRUCTURE_SNAPSHOT_OUT_INVALID`), is under
1591
+ an ignored root (`STRUCTURE_SNAPSHOT_OUT_IGNORED`) or already holds different
1592
+ bytes (`STRUCTURE_SNAPSHOT_OUT_EXISTS`). The candidate is validated with the
1593
+ rules the check applies to a signed snapshot before any byte is written, and a
1594
+ refusal leaves no new or changed file. An `--out` that already holds the same
1595
+ bytes is left as it is and the command succeeds. It writes only `--out`, never
1596
+ `project.json`; `signoff parts-review --draft <id> --structure-file <path>`
1597
+ then binds the file.
1598
+ The snapshot must live outside ignored roots, such as `evidence/`; the writer
1599
+ refuses paths or aliases into ignored storage. The writer hashes the retained
1600
+ file and checks its config/declaration identity when the draft config is
1601
+ available. A clone still checks the snapshot hash/schema and its recorded
1602
+ config identity, without requiring absent regenerable draft artifacts. It
1603
+ stores `draft_id` and `structure:{path,sha256}` on the parts-review sign-off.
1604
+ It does not apply patches or measure geometry. The later interview reads this
1605
+ human-approved snapshot instead of re-asking its structure as a guess.
1606
+ `signoff rigging-interview` resolves its `accepted_parts:{signoff_at,evidence}`
1607
+ and `structure` from the live human parts review and stores a shared
1608
+ `interview.rigging_context`, outside the answer union. Fresh rig guides must
1609
+ follow that approval and precede their rigging signature. Contradictory retained
1610
+ production config declarations require renewed approval rather than a silent
1611
+ override. Each production version uses the latest structure approval live at
1612
+ its build date, with reopen supersession evaluated at that same date; a later
1613
+ approval does not impose its declarations on old versions. Both new-order writers validate the full candidate before writing.
1614
+
1615
+ Mesh sign-off UI evidence from review#365 is optional in this contract, not a
1616
+ shipped-capability claim or mandatory gate. Review/producer owners validate the
1617
+ actual editors and mesh rendering. Create's synthetic tests prove record
1618
+ binding and refusals, not the physical correctness of a chest rebuild.
1619
+
1620
+ The synthetic new-order record and draft declaration files reproduce with
1621
+ `bun scripts/generate-example-order.ts <fresh scratch dir> --check`. The frozen
1622
+ legacy record is byte-preserved; generated evidence is never hand-written.
1623
+
1624
+ The [synthetic bound chain](../templates/example-chain/README.md) carries one
1625
+ project from its labelled draft and approved parts through a packaged rig,
1626
+ rig-review, signed post interview, authored direction and direction-review.
1627
+ `bun scripts/generate-example-chain.ts <fresh scratch dir> --check` reproduces
1628
+ it. The public writers record signoffs, rig/post review verdicts, the review
1629
+ package, immutable input inventory and authored direction. The parts verdict
1630
+ is a synthetic review-tool export from `tests/parts-package.ts`, recorded as a producer step.
1631
+ `chain.json` names seed declarations covering forward stage movement,
1632
+ `plan.steps (setup and status progress)` with `split.parts`, the rig-version
1633
+ declaration. Its source inventory is written by `input add`. Each seed
1634
+ declares its consuming command and records that command's step index. Status progress is forward-only,
1635
+ and signed plan fields stay bound. Post steps are added after rig-review,
1636
+ before the post-interview options.
1637
+ The direction binds that package's compiled model, built from the approved
1638
+ parts. Its comparison report records the identical painting and assembly.
1639
+ The retained setup still is the assembly recomposite. Rendered motion and
1640
+ camera behaviour require separate producer validation.
1641
+
1642
+ ## Correction paths: described and made
1643
+
1644
+ A correction lives in project-level `corrections[]`, including at `parts-review`
1645
+ before a production version exists. Adopt explicitly with `correction --adopt`;
1646
+ this writes `corrections_contract: {schema: "rig-create/corrections@1", adopted_at}`.
1647
+ No stage or interview-order schema changes. New projects and the filled example
1648
+ adopt it with an empty list. Older records get `CORRECTION_PATH_LEGACY` without
1649
+ inventing corrections, rewriting dates, or changing their existing origins.
1650
+
1651
+ Every record has `schema: "rig-create/correction@1"` (or `@2` for authored
1652
+ direction descriptions and made direction patches), `id`, `path`, `by`, `at`,
1653
+ `verbatim`, optional text `source`, `stage`, `on`, `artifact`, `outcome` and
1654
+ `resolves`. `path` is `described` or `made`. The stage is `parts-review`,
1655
+ `rig-review` or `direction-review`. `on` is the source identity; `outcome:null`
1656
+ means open. A closure sets outcome once and adds `closure:{by:"agent",at,reasoning}`
1657
+ with create's clock. That note is an agent declaration, never a human quote or
1658
+ acceptance. `resolves:null` or an earlier correction id records an append-only
1659
+ replacement; the predecessor remains history and is discharged only by a
1660
+ completed valid replacement.
1661
+
1662
+ - `described` uses `artifact:{kind:"description"}` and unchanged human words.
1663
+ - A made preview uses `artifact:{kind:"config-patch",patch:{path,sha256},
1664
+ state:"preview/pending",feedback:null}`. Its raw file hash is its own identity;
1665
+ it makes no claim about review's `patchSha256`.
1666
+ - A made session uses `artifact:{kind:"config-patch",state:"preview/pending",
1667
+ feedback:{file:{path,sha256},item_id}}`. The patch is the selected session
1668
+ item's `context.applied.patch`. With a bound result and passing producer
1669
+ summary, closure changes state to `applied-and-checked`. An optional retained
1670
+ preview `patch` must be parsed-deep-equal to the embedded patch. Create retains
1671
+ `applied.patchSha256` as review's claim and never recomputes it.
1672
+ - Other made operations use `{kind:"unsupported",operation,reason,files,
1673
+ producer_schema}`. `operation` is `mesh-delta`, `retarget`, `keyed-animation`
1674
+ or `other`. For example, `rig-review-applied/3` is a mesh delta, not a config
1675
+ patch. Its opaque producer evidence is retained; completion stays pending.
1676
+
1677
+ The pinned patch is exactly `rig-review-config-patch/1` with the five keys
1678
+ `schema`, `config_sha256`, `ops`, `producer:{name,version}`, `op:{cmd,args}`.
1679
+ `ops` contains 1–256 add/replace `{op,path,value,name?}` or remove `{op,path,name?}`
1680
+ operations, with nonempty RFC 6901 pointers and the published non-path flag
1681
+ allowlist. The session is `rig-review-session/1`, the checked patch Apply is
1682
+ `rig-review-applied/2` and its summary is `rig-review-check-summary/1`. Source
1683
+ config, inspected model and handoff input hashes bind to the source. Summary
1684
+ `pass:true`, `exitCode:0`, `gate:true`, a nonempty line and at least one `PASS`
1685
+ bar are required. A named `FAIL` or a status outside parts 2.16.0's
1686
+ `PASS | FAIL | SKIP | WARN | CLEAR | REPORTED | UNCLASSIFIED` set refuses.
1687
+ A bar named skipped by the producer line cannot be `PASS` in the map.
1688
+ Report-only SKIPs may be absent from that line. All accepted statuses remain
1689
+ verbatim by name. Resolution reasoning and text output state the PASS count,
1690
+ every non-PASS name/status and the retained producer line. The
1691
+ outcome's retained compiled-model SHA must equal `applied.modelHash`. A built
1692
+ preview or unknown/failed check cannot claim checked completion. Optional
1693
+ published route/files/environment context is retained. Optional `stageInputHash`
1694
+ must be a 64-lowercase-hex SHA-256, accepted as recorded; create cannot re-derive it from the package.
1695
+ Summary `source` may be absent or `remesh`, `retune`, or `check`, under the same producer-summary gates. No patch application,
1696
+ producer measurement, build or diff is recomputed by create.
1697
+
1698
+ @1 identities are `{kind:"rig"|"post",n,config:{path,sha256},model:{path,sha256}}`,
1699
+ `{kind:"parts",manifest:{path,sha256},files:[{path,sha256}]}`, or
1700
+ `{kind:"draft",id,config,parts,model}` (parts is `{manifest,files}`).
1701
+ A rig binds to its version's provenance config and frozen review package model.
1702
+ An @1 post binds config to `provenance.config`, and model to one declared retained
1703
+ output explicitly selected by `--model-file`; it is output identity only,
1704
+ without a parts, rig or CG claim. `config-patch/1` is rig/draft-only: a direct
1705
+ post edit is unsupported. A described post closure requires changed output bytes
1706
+ under @1, or a different bound direction document hash under @2.
1707
+ Parts identities retain a complete sorted parts.json + parts/ file inventory.
1708
+ Draft identities match the labelled draft inventory, including a retained
1709
+ compiled model when that reviewed identity is selected. Missing regenerable
1710
+ files never count as correction completion: retain source and result bytes.
1711
+ Paths must be literal project-relative regular files without symlink traversal;
1712
+ patch/session evidence belongs in tracked evidence. Outputs and draft files
1713
+ must survive handoff/clone when used to close a correction.
1714
+
1715
+ An ordinary correction may name the historical version actually inspected.
1716
+ An explicit `resolves` replacement must name the latest source at its capture
1717
+ time; a future source timestamp always refuses.
1718
+
1719
+ A result must be distinct, later and changed. Version and draft times bind the
1720
+ ordering; for raw parts, create's later closure records the declared response
1721
+ and changed inventory without inventing a producer timestamp. A rig-version
1722
+ made result retains the existing matching `review_origin` chain, including its
1723
+ immediate-predecessor constraint. A first rig may still have `review_origin:null`;
1724
+ no fake preceding rig is made for stage-1 corrections. `on_items` still names
1725
+ interview answers and `answers_feedback` still links verdicts; neither is an
1726
+ operation inventory. A correction does not supply a missing guide citation.
1727
+
1728
+ ```sh
1729
+ rig-create correction --adopt <dir>
1730
+ rig-create correction record <dir> --id refine --path described --stage rig-review \
1731
+ --version 1 --by reviewer --verbatim-file words.txt
1732
+ rig-create correction record <dir> --id tune --path made --stage rig-review \
1733
+ --version 1 --by user --patch-file evidence/patch.json --verbatim-file words.txt
1734
+ rig-create correction record <dir> --id applied --path made --stage rig-review \
1735
+ --version 1 --by user --feedback-file evidence/session.json --item change-1
1736
+ rig-create correction resolve tune <dir> --version 2 --by agent --reasoning-file reasoning.txt \
1737
+ --applied-feedback evidence/session.json --feedback-id change-1
1738
+ ```
1739
+
1740
+ Use `--draft <id>` or `--parts-file <retained parts identity JSON>` instead of
1741
+ `--version`; post versions require `--model-file`. `--resolves <id>` appends a
1742
+ replacement. A made record requires `--by user`; its clock is create's ingest
1743
+ time. A session item supplies its exact summary as verbatim, otherwise use
1744
+ `--verbatim-file`. Review does not invoke create or write project.json. There
1745
+ is no new review correction-export schema and no `--correction-file`.
1746
+ Record, resolve and check share validation; writes use a same-directory temporary
1747
+ file and atomic rename. Refusals leave project.json byte-identical. Unrelated
1748
+ pre-existing interview holes do not prevent capture, but a writer cannot add a
1749
+ new refusal or bypass correction validation.
1750
+
1751
+ Stage-1 corrections use parts/drafts outside versions. The parts-review signoff
1752
+ must approve the corrected draft and structure snapshot (or a draft using the
1753
+ corrected parts inventory). Production versions still compare mesh declarations
1754
+ to that snapshot. Structure changes after approval require the existing reopen
1755
+ and fresh snapshot, never a correction override.
1756
+
1757
+ A valid reopen supersedes the same stage suffix as signoffs: entries at or
1758
+ before its epoch remain history. A narrower later reopen cannot resurrect an
1759
+ earlier suffix. Recording under a live scoped approval requires reopening first;
1760
+ corrections do not move stages or sign plan steps themselves.
1761
+
1762
+ | State | check | approval / handoff | reopen | provenance | structure | CG |
1763
+ | --- | --- | --- | --- | --- | --- | --- |
1764
+ | described-open | pending/WARN | pending; attempted approval refuses | pass history | pending response, no citation authority | pass existing checks | pass existing checks |
1765
+ | described-linked | pass declared linkage | pass correction gate only | pass history | pass linkage, citations still required | pass if snapshot matches, else refuse | pass if ordinary gates pass, else refuse |
1766
+ | made preview/pending | pending/WARN | pending; attempted approval refuses | pass history | pending | pass current snapshot only | pass current config only |
1767
+ | made applied-and-checked | pass exact result binding | pass correction gate only | pass history | pass linkage, not a replacement citation | pass if snapshot matches | pass if ordinary gates pass |
1768
+ | invalid checked claim | refuse | refuse | no authority | refuse claimed linkage | mismatch refuses | no fabricated gate pass |
1769
+ | unsupported | pending/WARN | pending; attempted approval refuses | pass history | pending opaque evidence | pass existing checks | pass existing checks |
1770
+ | stage-1 parts/draft overlay | pass retained identity; absent closure bytes refuse | pending until corrected draft/parts is approved | parts-review suffix | identity only | pass approved corrected snapshot | pass / not applicable to parts |
1771
+ | rig-version overlay | pass source/result hashes | pending until correction and normal review complete | rig-review suffix | existing citations required | existing snapshot comparison | normal gates required |
1772
+ | direction-review post overlay | pass retained declared output | pending until changed output and normal review complete | direction-review suffix | post citations still required | no new rig claim | no new CG claim |
1773
+ | validly superseded overlay | pass history validation | pass exclusion from current obligations | pass only for valid human reopen | historical only | historical context | existing historical gates remain |
1774
+
1775
+ The overlays combine with path state; refuse overrides pending, which overrides
1776
+ pass. A pass never waives existing gates or manufactures acceptance.
1777
+
1778
+ The small consumer fixture is `tests/fixtures/correction-path/`, generated through
1779
+ writers by `scripts/generate-correction-fixture.ts <fresh dir> [--check]` before
1780
+ full-example integration. Its checked positive is explicitly **synthetic
1781
+ shape-only**, not a measured producer-gate result; see its README. Every retained
1782
+ file uses its real byte hash. The frozen legacy example's exact warning set is
1783
+ `QUESTION_SET_STALE`, `STAGE_ORDER_LEGACY`, `CHOICE_ANSWERS_LEGACY`,
1784
+ `CORRECTION_PATH_LEGACY`, and it is refused by the build-identity gate and by
1785
+ `SIGNOFF_TARGET_MISSING` twice and `SIGNOFF_ANSWERS_MISSING` four times as described above; the current filled example stays at 0/0.
1786
+
1787
+ ### Masked part-image repaints
1788
+
1789
+ When `assemble` levers are exhausted and one part image needs a bounded hand pass
1790
+ before `parts rig`, record the edit as a `made` correction at `parts-review` on
1791
+ the parent draft before registering the rebuilt draft:
1792
+
1793
+ ```sh
1794
+ rig-create correction record <dir> --id repaint-topwear --path made --stage parts-review \
1795
+ --draft <parent-id> --part topwear --part-image-file evidence/topwear-after.png \
1796
+ --mask-file evidence/topwear-mask.png --tool "gemini-image-probe r5 + mask composite with tone match"
1797
+ ```
1798
+
1799
+ The record carries `by: "agent"`, `verbatim: null`, and
1800
+ `artifact: { kind: "part-image", draft, part, before, after, mask, tool }`, where
1801
+ `before`, `after` and `mask` are `{ path, sha256 }` bindings and `tool` is free
1802
+ text naming the lever used. Its `on` (and an explicit `outcome`) is the draft
1803
+ identity `{ kind: "draft", id, config, parts, model: null }`: a part-image edit
1804
+ changes one part file, so it binds the draft's parts manifest and parts files and
1805
+ no compiled model, and the parent draft may be one recorded from a standalone
1806
+ `parts rig` output with no `skeleton.model.json`. Whether a later draft changed
1807
+ is read from its parts inventory. A part-image record that bound a model is
1808
+ still read, and its model must match the draft. Other draft corrections (`described`, a config
1809
+ patch Apply) still bind the draft's one retained compiled model. Both the writer and `check` decode the PNGs and
1810
+ refuse when `before` is not the parent draft's bound `<parts.path>/parts/<part>.png`
1811
+ or `after` makes no change (`CORRECTION_PART_IMAGE_BEFORE`), when `mask` or
1812
+ `after` dimensions differ from `before` (`CORRECTION_PART_IMAGE_MASK_SIZE`), when
1813
+ `after` differs from `before` outside `mask` (`CORRECTION_PART_IMAGE_OUTSIDE_MASK`),
1814
+ or when `after` increases alpha at any pixel (`CORRECTION_PART_IMAGE_ADDED_ALPHA`).
1815
+ A later `draft-rig` (new id) whose bound `<parts.path>/parts/<part>.png` matches
1816
+ `after.sha256` resolves the correction automatically (or explicitly via
1817
+ `correction resolve --draft <new-id> --by agent --reasoning-file <file>`). A draft
1818
+ whose bound `parts/<part>.png` matches a `part-image` correction's `after.sha256`
1819
+ whose correction is withdrawn, superseded, dated after the draft, or on another
1820
+ part/draft refuses as `DRAFT_PART_REPAINT_UNRECORDED`.
1821
+
1822
+ ### Authored camera direction (create#92)
1823
+
1824
+ Create owns the authored camera and timing document, not depth or `config.turn`.
1825
+ Review previews through the published `@rig-c/play` runtime (review README,
1826
+ *delivery flow*); create renders nothing. A document is authored data, not proof
1827
+ of a rendered take, seamless looping, source-animation extent, or a parts gate.
1828
+
1829
+ Adopt with `rig-create direction --adopt <dir>`. It writes
1830
+ `direction_contract:{schema:"rig-create/direction-contract@1",adopted_at,
1831
+ grandfathered_posts}`. The sorted, unique post numbers are existing posts at
1832
+ adoption. They remain exempt from a missing direction document; explicit
1833
+ bindings are always checked. There is no date switch and no new legacy warning.
1834
+ Do not change that list to exempt new work. Existing correction@1 records are
1835
+ not migrated or rewritten.
1836
+
1837
+ ```sh
1838
+ rig-create direction write 5 <dir> --file evidence/authored-direction.json --rig 2
1839
+ rig-create direction check 5 <dir> --json
1840
+ ```
1841
+
1842
+ The writer registers a new post version and never overwrites a previous one.
1843
+ The exact input bytes become `evidence/direction/v5.json`, schema
1844
+ `rig-create/direction@1`. `provenance.config`, `provenance.direction`, and one
1845
+ `outputs[]` entry all bind that same `{path,sha256}`. It is a direction output,
1846
+ not a compiled model or movie. `provenance.sections.camera` and `.timing` carry
1847
+ the same citations as the document. Metadata, rig bindings and citations are
1848
+ not additional executable config sections. Setup-painting requirements apply
1849
+ to existing build configs as before, not to this camera document.
1850
+
1851
+ The closed document has precisely:
1852
+
1853
+ | Field | Closed contents |
1854
+ | --- | --- |
1855
+ | `schema` | `rig-create/direction@1` |
1856
+ | `rig` | `{version,model:{path,sha256}}`; accepted rig's review-package compiled model, never config-only identity |
1857
+ | `take` | `{duration,loop}`; positive finite seconds and boolean |
1858
+ | `camera` | `{space:"rig-world-y-up",keys:[{time,box:{x,y,width,height},easing}]}` |
1859
+ | `timing` | `{keys:[{time,source_time,easing}]}` |
1860
+ | `citations` | `{camera,timing}`; each `{guide,asks,quote}` or `{guide,asks,delegated:true}` |
1861
+
1862
+ The model path is
1863
+ `derived/review/v<R>/out/check/build/skeleton.model.json`; its exact bytes must
1864
+ match the corresponding review-package inventory SHA. A new write needs the
1865
+ latest live accepted rig and existing handoff still/signature gates. Missing
1866
+ model bytes refuse; create never regenerates them. A post's `rig_version` must
1867
+ match `rig.version`. Historical documents may retain their historical rig;
1868
+ current authoring and direction approval cannot use a superseded rig.
1869
+
1870
+ Boxes describe the visible rectangle in rig world coordinates, Y up. Coordinates
1871
+ are finite and sizes positive. A preview contains the box at its aspect ratio
1872
+ (letterbox; do not silently crop). Camera keys number 1–256, start at zero,
1873
+ strictly increase and stay in `[0,duration]`; the final box holds through the
1874
+ end. Timing keys number 2–256, start at zero and end at `duration`; their take
1875
+ times strictly increase, while finite nonnegative `source_time` is nondecreasing
1876
+ and permits holds. It maps take seconds to source-animation seconds, without
1877
+ FPS inference or a source-clip-duration claim. `loop` repeats the take clock.
1878
+
1879
+ Easing is exactly `linear`, `hold`, or `smoothstep`, attached to the segment
1880
+ starting at the key. `smoothstep(u)=u*u*(3-2*u)` with normalized segment time;
1881
+ interpolate box components/source time with that factor. The final key's easing
1882
+ has no effect. No arbitrary easing, pan/zoom alternative, audio, depth, tilt,
1883
+ turn, frame-index inference or extension fields exist in @1.
1884
+
1885
+ Citations name the actual questions `camera_depth` and `post_timing`, each with
1886
+ `asks:[guide]`. `camera` and `timing` are executable plan step ids, not question
1887
+ ids. Quotes are exact stored verbatim/additions fragments, including historical
1888
+ or inherited answers; delegation uses the existing decided-delegation rules.
1889
+ A new write additionally requires a live post-interview approval covering both
1890
+ steps with matching `step_hashes`, and resolved current per-ask selections.
1891
+ Pending auto/Other responses cannot produce a document. A camera marked not
1892
+ applicable can supply a single fixed-box key only when its retained verbatim
1893
+ is cited and the approved post plan still supplies camera/timing; timing must
1894
+ be applicable. All-post-not-applicable skips post entirely. Prose is never
1895
+ parsed into authored key values.
1896
+
1897
+ Writers validate the prospective record and every owned finding before writing,
1898
+ retain unrelated pre-existing holes by code/path multiset, and reject concurrent
1899
+ source changes. Documents are immutable; `project.json` is the commit point.
1900
+ A lock, staged files, snapshot rechecks and rollback protect a failed publish.
1901
+ This is not an atomic multi-file filesystem transaction. A refused write leaves
1902
+ the record and previously published artifacts byte-identical. Source documents
1903
+ and patches are literal retained POSIX paths below the selected project, without
1904
+ symlinks, traversal or ignored roots. Only the explicitly bound compiled-model
1905
+ path above is allowed in regenerable `derived/`. Clone it when using this gate.
1906
+ The scene's idle frames are the one path admitted under `outputs/`: a post on
1907
+ rig `vN` reads `outputs/vN/idle/frames.json` only with the path and sha256 of
1908
+ `versions[vN].registration.idle`, and any other file under `outputs/vN/idle/`
1909
+ only with a `versions[vN].outputs` binding. The bytes stay where `rig-version`
1910
+ froze them; no second copy is retained. Present bytes are verified on every
1911
+ write and check. Absent bytes refuse, as the registration check refuses absent
1912
+ frozen outputs, so restore `outputs/vN/` before writing or checking the post.
1913
+
1914
+ ### Review reads; create ingests direction patches
1915
+
1916
+ Review receives a user-designated project root and post number `N`. It reads
1917
+ that version's `provenance.direction`, then exactly
1918
+ `evidence/direction/v<N>.json` as `rig-create/direction@1`, validating raw SHA,
1919
+ version/path binding and rig model SHA. `readDirection(dir,project,N)` is the
1920
+ create reader; `direction check N --json` exposes a checked read. Review never
1921
+ writes `project.json` or invokes create. Review 1.6.0 has no authored camera
1922
+ consumer; review#373 consumes this contract after it lands. No camera Apply
1923
+ capability is claimed for that installation.
1924
+
1925
+ This is a planned contract. No review version produces it today: the direction
1926
+ producer is review#373, which is parked, and its result will arrive as a job act
1927
+ (create#271). The planned patch is a closed `rig-review-direction-patch/1` with exactly
1928
+ `{schema,source_post,rig_model_sha256,direction_sha256,replace}`. `replace` holds
1929
+ one or more of `camera`, `timing`, `take`, replacing whole typed sections only.
1930
+ The source is the current post, and both base hashes must match. Unknown keys,
1931
+ arbitrary pointers and a semantic no-op refuse. Binding/citation changes need
1932
+ new answer/authoring work, not a patch. The patch's own identity is the raw hash
1933
+ of its retained bytes, not a review canonicalization claim.
1934
+
1935
+ ```sh
1936
+ rig-create correction record <dir> --id framing --path made --stage direction-review \
1937
+ --version 5 --direction-patch-file evidence/direction-patch.json \
1938
+ --by user --verbatim-file correction-words.txt
1939
+ rig-create direction patch 6 <dir> --patch-file evidence/direction-patch.json
1940
+ rig-create correction resolve framing <dir> --version 6 --by agent \
1941
+ --reasoning-file resolution.txt
1942
+ ```
1943
+
1944
+ The patch writer creates a later immutable document/post and records
1945
+ `direction_origin:{source_post,patch:{path,sha256},result:{path,sha256}}` on the
1946
+ version. Capture retains the human words separately; no words are invented from
1947
+ the patch. Review may label export plus create ingestion “Apply”, but cannot
1948
+ claim ingestion or checked completion itself. `rig-review-applied/2` is never
1949
+ used as a direction result or proof.
1950
+
1951
+ Authored-direction descriptions and `direction-patch` use
1952
+ `rig-create/correction@2`. Other correction records remain
1953
+ `rig-create/correction@1`, with existing config-patch and unsupported behavior.
1954
+ The made patch artifact is
1955
+ `{kind:"direction-patch",patch:{path,sha256},state:"preview/pending"|
1956
+ "applied-and-checked",result:null|{path,sha256}}`. Its exact post source/outcome
1957
+ identity is `{kind:"post",n,config,model,direction:{path,sha256}}`; config binds
1958
+ the direction file, model binds the source rig's compiled model, and direction
1959
+ matches `provenance.direction`. This identity is not accepted by @1.
1960
+
1961
+ A pending record has no outcome/result and cannot approve. On agent resolution,
1962
+ create checks its own patch/base/result and `direction_origin`, not an external
1963
+ producer report. The outcome is a later post on exactly the same rig model,
1964
+ with changed direction bytes and the typed replacements actually present.
1965
+ Same-document, cross-kind, changed-rig or unrelated authored output pairs
1966
+ refuse for made patches.
1967
+
1968
+ A described request at direction-review on a post with authored direction
1969
+ also binds this exact identity, using `artifact:{kind:"description"}`. Record it
1970
+ with `--version`; an optional `--model-file` must name the source rig's compiled
1971
+ model. Selecting the direction JSON as model refuses before writing. Resolve
1972
+ it to a later authored post with a different bound direction document hash
1973
+ through `correction resolve --version`. This includes a later accepted rig
1974
+ because direction@1 embeds `rig.model`. The agent's closure reasoning carries
1975
+ the semantic claim that the request was met. No patch or `direction_origin`
1976
+ is required for a described request. Both source and outcome use
1977
+ `validateDirectionIdentity`. An existing @1 description cannot resolve with
1978
+ the direction document named as model. Capture a new @2 described record to
1979
+ close that legacy request.
1980
+
1981
+ `DESCRIBED_DIRECTION_SINCE = 2026-11-06T00:00:00Z` dates existing @1 authored
1982
+ direction descriptions by their original `at`. Earlier records receive
1983
+ `DESCRIBED_DIRECTION_LEGACY`; contemporary records receive
1984
+ `DESCRIBED_DIRECTION_REQUIRED`. Preserve history and capture the next request
1985
+ through the writer. Posts without authored `provenance.direction` keep their
1986
+ existing @1 declared-output identity. Rig, draft and parts identities are
1987
+ unchanged. Unsupported operations remain pending.
1988
+
1989
+ `checkProject`, direction signoff and the acceptance writer all check the
1990
+ relevant document. Handoff stays rig-only to bootstrap authoring. Review-package
1991
+ stays rig-only and cannot rebind a model already cited by a direction document.
1992
+ Reopen rules, historical evidence, human signoff and agent closure remain
1993
+ separate gates.
1994
+
1995
+ The generated geometric example is `templates/example-direction/`, including a
1996
+ small staged project, exact input/result documents, a review-format patch and
1997
+ @2 correction. It uses invented sentences shaped like a pilot interview, not
1998
+ pilot words or assets. Reproduce with
1999
+ `bun scripts/generate-direction-fixture.ts <fresh-dir> --check`. It demonstrates
2000
+ writer/contract behavior with a real installed `rigc build` of a public rectangle
2001
+ probe. Its compiled local yaw slider is readable by review's `discoverTurnDials`
2002
+ (`src/turn-pad.ts`, revision `c3e571db1878093b183c282b56f5eb846c1d742b`),
2003
+ using core 3.2.1's `RigSliderConstraint` (`src/rig.ts`). The compiled model has
2004
+ no authored camera capability; camera requests in the direction documents are
2005
+ unsupported playback requests, not evidence of camera rendering or Apply.
2006
+ The installed core-only build reports `A00_ROUNDTRIP_PARSE` as SKIP; this is
2007
+ not a spine-core round-trip or a rendered-take claim. Recompute every recorded
2008
+ file hash with `bun scripts/check-direction-fixture-hashes.ts`; entries of a
2009
+ producer run stamp (`rigc-run/1`, `rig-run/1`) resolve against the stamp's own
2010
+ directory.
2011
+ The main filled project remains the separate zero-refusal/zero-warning example.
2012
+
2013
+ ## Advisory intake reports
2014
+
2015
+ `project.intake` is optional (`spec: "rig-create/intake@1"`). In `parts`, after
2016
+ a full run exists, retain the parts `head-crop-preflight` JSON and record it:
2017
+
2018
+ ```sh
2019
+ rig-create intake-report . --id crop-1 --report evidence/preflight.json --sha256 <raw-sha256> --parts-version 2.16.0 --by agent
2020
+ rig-create intake .
2021
+ ```
2022
+
2023
+ This reads producer bytes and never runs a producer or verifies its pixels.
2024
+ `reports[]` is append-only. Each entry keeps its id, by/at, a report binding
2025
+ (path, raw sha256, spec, head_box, source_canvas, run_canvas), `measurement`
2026
+ (the report's own fields copied exactly), `prediction` (`hit` or `no-hit`),
2027
+ and the declared `parts_version`. Version is outside the authenticated v1
2028
+ report, which does not emit a version. Correct a report with a new retained
2029
+ path/id and `--supersedes <earlier-id>`; do not replace its bytes or history.
2030
+ The writer validates before writing and leaves project bytes unchanged on
2031
+ invalid input. Copied fields are checked against the bound report, never
2032
+ against a fresh computation.
2033
+
2034
+ A HIT is any reported `layers[].sides.top_px`, `left_px` or `right_px` > 0.
2035
+ Keep the per-tag counts, including zero rows and separate `bottom_px`.
2036
+ Corners count on two sides; summed edges are incidences, not unique pixels.
2037
+ Preflight counts native full-run alpha > 8 **before ghost cleanup** and
2038
+ retains registration and canvas identity. `ASSEMBLE_CROP_CLIPPED` counts
2039
+ alpha ≥ 128 **after ghost cleanup**; it is a separate instrument. Never
2040
+ compare or substitute their numbers. Intake only stores the preflight
2041
+ instrument; downstream outcome labels stay in evidence receipts, not an
2042
+ in-project label ledger. Missing labels remain unknown, never negative.
2043
+
2044
+ Everything about persisted intake is advisory: HIT, absent choice, malformed
2045
+ record or unavailable report bytes cannot refuse signoff, acceptance or
2046
+ handoff. A retrospective over three pilots fired on all three; only one had
2047
+ a labelled positive and none a labelled negative. This shows firing, not
2048
+ ability to discriminate. The threshold stays > 0; it is not an approval gate.
2049
+ The filled example does not adopt intake and keeps zero warnings.
2050
+
2051
+ Colour-adjacent ground and ground enclosed between strands are
2052
+ **known-not-predicted** notes in the guide/intake output only (two rounds of
2053
+ a model sweep on a pilot did not separate them), with no threshold or warning.
2054
+ Silhouette stepping, source-vs-final alpha classes, patchwork runs and
2055
+ versioned dark retention need post-assemble outputs and are excluded.
2056
+
2057
+ ### Targets of review signatures
2058
+
2059
+ `signoff rig-review` and `signoff direction-review` accept `--version <n>`.
2060
+ Without it, exactly one candidate must exist. Candidates are versions of the
2061
+ review's kind with an accepted verdict and no live same-stage signature
2062
+ already targeting them. A refusal lists candidates without a recency preference.
2063
+ An explicit version must exist with the right kind and an accepted verdict.
2064
+ A rig version that a reopen or amendment of `rig-review` or an earlier stage
2065
+ made history is no candidate. When that leaves no live rig version accepted,
2066
+ `signoff rig-review` refuses before it writes (`RIG_VERSION_UNACCEPTED`),
2067
+ naming the lapsed acceptance and its `reopens[]` or `amendments[]` entry;
2068
+ register a new rig version and accept it first. A signature recorded before
2069
+ this refusal existed is not refused at `rig-review`: `advance` and `handoff`
2070
+ stop on it with the same code.
2071
+
2072
+ Each signature records `target.version` and `target.verdict`, the literal
2073
+ `versions[i].verdicts[j]` path of the accepted verdict. A rig review adds
2074
+ `target.review_package = {path: "versions[i].review_package", sha256}`. Its hash
2075
+ is SHA-256 of UTF-8 `JSON.stringify(sorted({config, files}))` from the inline
2076
+ package record. Keys sort recursively, arrays retain order, and `from` and
2077
+ `open` are excluded. Package checks still resolve the retained files on disk.
2078
+
2079
+ An adopted authored direction adds `target.direction = {path, sha256}` from
2080
+ the version's `provenance.direction` and the retained document bytes. A post
2081
+ without an adopted document instead adds `target.post_config`, copied from
2082
+ `provenance.config`. Exactly one is present. The latter is compared with the
2083
+ version's current binding, while existing provenance checks own file reads.
2084
+ Writers validate targets before writing. Refusals leave project.json identical.
2085
+
2086
+ From `SIGNOFF_TARGET_SINCE = 2026-11-06T00:00:00Z`, missing and stale targets
2087
+ refuse as `SIGNOFF_TARGET_MISSING` and `SIGNOFF_TARGET_STALE`. Earlier signatures
2088
+ receive one corresponding `_LEGACY` warning each. Amendment lapse rules continue
2089
+ to apply to these approvals.
2090
+
2091
+ Pre-interview rig versions bind `target.outputs = {path: "versions[i].outputs",
2092
+ sha256}` using SHA-256 of UTF-8 `JSON.stringify(sorted(outputs))`. A declared
2093
+ `outputs_lost` refuses signing because nothing survives to bind. Any target has
2094
+ exactly one of `review_package`, `outputs`, `direction` or `post_config`.
2095
+ The operative verdict is the last human, non-provisional verdict for that
2096
+ review stage. It must be accepted, and the target must name that exact verdict.
2097
+ A later human bounce makes the earlier bound acceptance stale.
2098
+
2099
+
2100
+ ## Scene composition and direction@2
2101
+
2102
+ `scene write --rig <R> --background <path> [--foreground <path>]
2103
+ [--background-rect x,y,w,h] [--foreground-rect x,y,w,h]
2104
+ --citations-file <file> [<dir>]` derives the latest accepted rig,
2105
+ live rig-review signoff target, compiled model and stage. It appends
2106
+ `scenes[]` entries `{n,path,sha256,at,by}` and immutable
2107
+ `evidence/scene/s<N>.json` documents with schema `rig-create/scene@1`.
2108
+ New entries record `by:"agent"`. A typed `--by` is refused: human authority
2109
+ comes from the current ingested `post_composition` Review choice or its decided
2110
+ delegation. Earlier human-authored scenes retain their existing validation;
2111
+ no contract date or additional historical warning is introduced.
2112
+ The project append commits the scene. Unreferenced files are ignored.
2113
+ The latest entry is current. Existing documents stay byte-identical.
2114
+
2115
+ The closed document contains `schema`, `n`, `rig`, `frame`, `plates`, `layers`.
2116
+ `rig` contains `version`, `acceptance`, `model` and `animation`.
2117
+ `acceptance` retains `signoff:{stage:"rig-review",checkpoint:null,at}`,
2118
+ `verdict` and `review_package:{path,sha256}` from the live signoff target.
2119
+ `model` is the accepted package's compiled `{path,sha256}` binding.
2120
+ The writer selects `animation.name:"idle"`. `animation.frames` is present
2121
+ exactly when the version declares `registration.idle`, and equals that binding.
2122
+ Each SHA-256 binds raw file bytes.
2123
+
2124
+ `frame.space` is `rig-world-y-up`. `frame.stage` copies the compiled model's
2125
+ stage field for field. Plate `rect:{x,y,width,height}` uses rig world units
2126
+ and its bottom-left corner. The selected `value` (or delegated decision value)
2127
+ must contain `scene:{background:{path,rect},foreground?:{path,rect}}`. The writer
2128
+ derives omitted rect flags from that exact map; supplied roles, paths and rects
2129
+ must match it exactly. It never infers placement from prose, a singleton input
2130
+ or the stage. Rects may extend past the stage and require uniform scale within
2131
+ relative 1e-9. Exact choice equality applies even within that scaling tolerance. Plate `width` and `height`
2132
+ come from PNG IHDR. Declare one background and at most one foreground, with
2133
+ distinct paths and hashes. `layers` is `["background","rig"]` or
2134
+ `["background","rig","foreground"]` accordingly. A declared foreground
2135
+ is validated fully.
2136
+
2137
+ The citations file is a map of the declared roles to
2138
+ `{guide:"post_composition",asks:["post_composition"],quote:"recorded words"}`
2139
+ or the same citation with `delegated:true` replacing `quote`.
2140
+ Each plate stores this human designation, its path, sha256, pixel size, rect,
2141
+ and `registered:{kind:"input"|"derived",ref:"inputs[i]"|"derived[i]"}`.
2142
+ Inputs must match by path and hash. A derived registration requires every
2143
+ `prepared-input` source/sources hop to match by path and hash and terminate
2144
+ in registered inputs. Missing result hashes and cycles refuse.
2145
+ Register a plate with `input add` before `scene write` names it. New agent
2146
+ scenes require input-writer metadata `{path,sha256,bytes,origin,at}` for raw input
2147
+ candidates, or a bound `rig-parts-plate-receipt/1` for derived candidates. A
2148
+ legacy input must be registered under a new source name with
2149
+ `input add <path> --origin …`, then selected through Review. Missing, stale or
2150
+ mismatched authority refuses before the project append. Quotes must cite the
2151
+ current ingested answer; historical answers remain available to historical
2152
+ scene readers. Composite preview presentation remains tracked in
2153
+ [Review #482](https://github.com/rig-c/review/issues/482); writer/checker success
2154
+ is not visual acceptance.
2155
+
2156
+ `rig-create/direction@2` keeps @1's camera, timing, take and citations and adds
2157
+ required `scene:{n,path,sha256}` equal to a committed scene entry. Its rig and
2158
+ model equal the scene's. Camera boxes and plates share rig-world-y-up units.
2159
+ Current authoring and the current live post must cite the latest scene.
2160
+ With no live post, no scene is current.
2161
+ SCENE_RIG_STALE, lapsed-signoff SCENE_ACCEPTANCE_STALE and
2162
+ DIRECTION_SCENE_STALE share one currentness gate. Every committed scene
2163
+ retains its historical human acceptance and matching review-package identity,
2164
+ plus all schema, file, model, animation, frame, plate, registration and citation
2165
+ checks. Reopening preserves these historical records. The next accepted rig
2166
+ gets a new scene number.
2167
+ Post identity gains `scene:{path,sha256}`. Patches retain their existing
2168
+ whole-section schema, so a scene change is a new post.
2169
+
2170
+ Existing direction-contract@1 adoption is preserved. Grandfathered posts
2171
+ need no document and bypass both direction schemas. Historical @1 documents
2172
+ remain readable. Earlier @1 posts warn with DIRECTION_SCENE_REQUIRED.
2173
+ Posts from DIRECTION_SCENE_SINCE require @2. The parent finalizes the one
2174
+ exported cutoff to the implementation's UTC landing day before merge and
2175
+ checks every live pilot for new same-day refusals.
2176
+
2177
+ ### Scene option context
2178
+
2179
+ A post option set takes one of two contexts: the rig kind
2180
+ (docs/INTERVIEW.md) or the scene kind. This section is the scene kind's
2181
+ contract.
2182
+
2183
+ 1. **Entries.** `project.scenes[]` is append-only. Each entry is exactly
2184
+ `{n,path,sha256,at,by}`: `n` counts from 1 in array order, `path` is
2185
+ `evidence/scene/s<n>.json`, `sha256` is the SHA-256 of the file's raw bytes
2186
+ in lowercase hex, `at` is a UTC ISO time ending in `Z`, and `by` is `agent`
2187
+ for new scenes or a retained human identifier for historical scenes. The documents are immutable `rig-create/scene@1`.
2188
+ 2. **Reference.** The context is `{kind:"scene",scene:{n,path,sha256}}`: the
2189
+ entry without `at` and `by`.
2190
+ 3. **Selection.** At an instant *t*, the candidate is the last `scenes[]`
2191
+ entry whose `at` ≤ *t*. Earlier entries are never selected.
2192
+ 4. **Currentness.** The candidate applies only if it is current at *t*.
2193
+ Its document must match `sha256`, carry the entry's `n` and have the closed
2194
+ scene@1 shape. Then the scene currentness gate (`SCENE_RIG_STALE`,
2195
+ `SCENE_ACCEPTANCE_STALE`) runs on the records dated at or before *t*:
2196
+ `signoffs[]`, `versions[]` and their `verdicts[]`, `reopens[]` and
2197
+ `amendments[]`. Undated entries are left out.
2198
+ - `rig.version` is the latest live accepted rig, and no live rig version
2199
+ has a higher `n`.
2200
+ - `rig.acceptance` equals the latest live rig-review signoff
2201
+ (`checkpoint:null`) whose target names that version: its
2202
+ `{stage,checkpoint,at}`, `verdict` and `review_package`.
2203
+
2204
+ No live post is needed. When the gate fails, the set takes the rig kind
2205
+ exactly as if no scene existed, so a scene built on an earlier rig never
2206
+ anchors a post set. The rig kind keeps its own refusals: with no live
2207
+ accepted rig and live rig-review signoff at *t*, for example after a
2208
+ rig-review reopen, it refuses `INTERVIEW_OPTIONS_STALE`. An unreadable,
2209
+ changed or malformed candidate refuses `INTERVIEW_OPTIONS_STALE` instead
2210
+ of falling back.
2211
+
2212
+ The closed scene@1 shape in this rule is exactly the structure below.
2213
+ "Closed" means the listed keys must be present, optional keys may be
2214
+ present, and no other key is allowed. A text value is a string with
2215
+ non-blank content. A count is a positive safe integer. A hash is 64
2216
+ lowercase hex digits. A path is a relative `/`-separated text with no empty,
2217
+ `.` or `..` segment, no leading `/`, no backslash and no drive prefix. A box is
2218
+ closed `{x,y,width,height}` with number values. A binding is closed
2219
+ `{path,sha256}`.
2220
+
2221
+ | Object | Keys (optional in brackets) | Values |
2222
+ | --- | --- | --- |
2223
+ | document | `schema`, `n`, `rig`, `frame`, `plates`, `layers` | `schema` is `rig-create/scene@1`; `n` is a count; `plates` is a list; `layers` is a list of strings |
2224
+ | `rig` | `version`, `acceptance`, `model`, `animation` | `version` is a count; `model` is a binding |
2225
+ | `rig.animation` | `name`, [`frames`] | `name` is text; `frames` is a binding |
2226
+ | `rig.acceptance` | `signoff`, `verdict`, `review_package` | `verdict` matches `versions[<i>].verdicts[<j>]`; `review_package` is a closed `{path,sha256}` with text path and a hash |
2227
+ | `rig.acceptance.signoff` | `stage`, `checkpoint`, `at` | `rig-review`, `null`, and a readable date text |
2228
+ | `frame` | `space`, `stage` | `space` is `rig-world-y-up`; `stage` is a box |
2229
+ | each plate | `role`, `path`, `sha256`, `width`, `height`, `rect`, `registered`, [`citation`] | `role` is `background` or `foreground`; `path` is a path; `sha256` is a hash; `width` and `height` are counts; `rect` is a box; `registered` is closed `{kind,ref}` with `kind` `input` or `derived` and text `ref` |
2230
+ | `citation` | any of `guide`, `asks`, `quote`, `delegated` | a string, a list of strings, a string, and a boolean |
2231
+
2232
+ A plate's file bytes, PNG size, registration chain, rect scale and
2233
+ citation wording, the layer order and the role count are validated when
2234
+ the scene is written and in the full scene check above. They are not part of
2235
+ this option-context gate, so a reader of option contexts doesn't have to
2236
+ reproduce them.
2237
+ 5. **Freshness.** These rules apply to the rig and scene kinds alike.
2238
+ - The context is recomputed at `drafted_at` and again at the selection or
2239
+ decision time, and both must equal `set.context` exactly. A writer uses
2240
+ now. `check` uses the answer's or decision's `at`, and `drafted_at` for a
2241
+ pending set. A later scene, or a later live rig version, makes a
2242
+ scene-kind set `INTERVIEW_OPTIONS_STALE`.
2243
+ - A rig-kind set is recomputed with `scenes[]` ignored, so no scene makes
2244
+ it stale.
2245
+ - A reopen or amendment of a stage at or before the set's stage, dated
2246
+ after `drafted_at` and at or before that time, refuses
2247
+ `INTERVIEW_OPTIONS_STALE`.
2248
+ - The file at `scene.path` must be a literal project-relative regular file
2249
+ whose raw bytes match `scene.sha256`.
2250
+ 6. **Contract date.** A scene-kind set drafted before
2251
+ `SCENE_CONTEXT_SINCE = 2026-11-08T00:00:00Z` that is stale only under
2252
+ rule 4 is checked under the rule of its day, where the latest scene
2253
+ applied whether current or not. It warns `INTERVIEW_OPTIONS_SCENE_LEGACY`.
2254
+
2255
+ ### Publication and review read boundary
2256
+
2257
+ Publication (#184) retains and raw-byte-seals the scene document, its model,
2258
+ plates and optional frames as `files[]` rows using the scene's hashes.
2259
+ Every texture page needed by the model is retained and sealed through the
2260
+ existing review-package inventory or published files[]. The scene adds no
2261
+ page field or second hash domain. Parts keep their manifest and image seals.
2262
+ `projectHash` seals raw project.json bytes in rig-review-job-source/2.
2263
+
2264
+ Review selects the scene from the retained project, an option context or
2265
+ direction document and resolves it against that publication's retained
2266
+ project root. Every dependency is read from that same snapshot.
2267
+ The files[] inventory does not select a scene.
2268
+
2269
+ For viewport `(vx,vy,vw,vh)` and scale `s`, Canvas projects a plate by its top
2270
+ edge. Destination left is `(rect.x-vx)*s`, top is
2271
+ `(vy+vh-rect.y-rect.height)*s`, width is `rect.width*s`, height is
2272
+ `rect.height*s`. Foreground is optional. Review #482 owns that consumer
2273
+ change and the inspection camera default. Create performs no rendering.
2274
+
2275
+ ## Corrections through Review feedback
2276
+
2277
+ For config corrections, `--feedback-file`, `--applied-feedback` and
2278
+ `--patch-file` now take the Review package argument to `rig-review feedback`,
2279
+ with `--item` or `--feedback-id` selecting the saved item. create passes that
2280
+ argument unchanged from the project directory and records the CLI argv.
2281
+ `--patch-file` selects the patch embedded in that applied item. A downloaded
2282
+ preview patch that Review has never applied has no API source. For example,
2283
+ a proposed config-patch JSON with no saved applied item must first be applied
2284
+ and submitted through Review before create can record it.
2285
+
2286
+ create stores the exact stdout at `evidence/review-reads/<sha256>.json`.
2287
+ The correction artifact's `review_read` contains `route: "cli"`, `argv`,
2288
+ `stdout: {path, sha256}` and `tool: {name, version}`. The `feedback.file`
2289
+ binding names those same stdout bytes. A version's `review_origin` uses
2290
+ `review_read` and `feedback` with the same bindings. `check` consumes these
2291
+ stored responses and does not invoke Review. Records before
2292
+ `REVIEW_READ_SINCE` without a receipt get `REVIEW_READ_LEGACY` and preserve
2293
+ their history without opening producer-saved session or patch files.
2294
+
2295
+ The field inventory used synthetic declared applied/2 through Review's
2296
+ browserless session PUT. Producer Apply was outside this inventory. The inventory confirmed
2297
+ `patch`, `patchSha256`, `configHash`, `modelHash`, `check` and `files` survive
2298
+ `feedback --format json`.
2299
+
2300
+ ### Recording interview picks from a standing job
2301
+
2302
+ Use `interview-answer <question> [<project>] --from-review <actId>` after the
2303
+ human finalizes an `interview-picks` act in the standing Review job. The writer
2304
+ fetches through its private sibling capability, binds the displayed option set
2305
+ by ID and SHA-256, and retains the immutable API act bytes in `review_acts`.
2306
+ An identical act can be replayed without adding a second answer. A changed act
2307
+ with the same identity refuses. `--why-file` still supplies the exact reason
2308
+ for a member override. The removed `--picks-file` flag refuses with
2309
+ `INTERVIEW_PICKS_FILE_REMOVED`. Earlier answers remain valid.
2310
+
2311
+ ## Model-free record publication
2312
+
2313
+ `readRecordPublished({root, record, result, phase})` in `job-publication.ts`
2314
+ implements create's local boundary for review#561 CP1. `record` binds the
2315
+ literal `project.json` path and its raw-byte `sha256`. `result` is either
2316
+ `{kind: 'option-set', questionId, setId}` or `{kind: 'plan'}`. Reader `phase`
2317
+ is `'inventory' | 'retained'`. A retained request also passes `parts`, the
2318
+ identity the inventory phase returned. The optional second argument is the check
2319
+ instant in epoch milliseconds, defaulting to the current clock.
2320
+
2321
+ The return value has exactly `{label, parts, inputs, files, plan, subjects}`. An
2322
+ option context has `plan: null` and exactly the requested
2323
+ `{kind: 'interview-picks', questionId, setId}` subject. A plan context has
2324
+ ordered `{id, hash}` step identities and eligible
2325
+ `{kind: 'checkpoint', checkpointId}` or `{kind: 'signoff', stage}` subjects.
2326
+ Subject key order follows these declarations. Identifiers must fit the
2327
+ review record-id domain of 1–128 ASCII letters, digits, dots, underscores
2328
+ and hyphens, starting with a letter or digit. The reader identity is
2329
+ `rig-create/publication-reader/2`. Create's current live host stays in
2330
+ `package-only` mode while installed Review 2.8.0 exports the public
2331
+ record-mode V3 symbols and types.
2332
+
2333
+ Eligibility comes from the one `checkProject` call with `readOnly: true`.
2334
+ Its effective stage must match the requested option set's stage. Plan
2335
+ signoffs are offered only for an unsigned effective `plan-approved`,
2336
+ `rigging-interview` or `post-interview` stage that closes by signoff.
2337
+ Checkpoint subjects require an enabled at-checkpoint step in the effective
2338
+ execution stage. An empty subject list permits plan inspection. Warnings
2339
+ remain eligible, and publication HOLEs refuse publication.
2340
+
2341
+ `inputs` projects every declared input to `{path, sha256}` in project-input
2342
+ order and rehashes its bytes. `files` seals the record, inputs and selected
2343
+ option/context evidence, and every project file the check read. Plans include
2344
+ current answer evidence for every displayed step's declared basis and retained
2345
+ parts/structure approval evidence. Signoff eligibility uses the existing answer-selection and
2346
+ rigging-context readers. Plan step hashes use `stepHash`.
2347
+
2348
+ Every path is literal and project-relative. Duplicate declarations within
2349
+ a list and conflicting hashes for a path refuse. Identical references share
2350
+ one file row. The file list is unique by path and sorted by JavaScript
2351
+ string comparison. Both phases verify closure bytes before the official
2352
+ check and write nothing, create no lock and perform no intent recovery.
2353
+ Default `checkProject` calls keep their existing recovery behavior.
2354
+
2355
+ The check's reads come from `checkProject(..., { recordReads: true })`: the
2356
+ check modules read project files only through the recorded reader in
2357
+ `src/check-reads.ts`, which returns each file the check opened, stat'ed,
2358
+ resolved or listed as `{path, sha256}`. The job's private outbox and
2359
+ `node_modules/` are not record files and are not recorded. A retained root
2360
+ holding exactly `files` therefore gives the check the same bytes, and the
2361
+ retained phase returns the inventory phase's context. A test-only strict mode
2362
+ (`tests/strict-reads.ts`) throws `CHECK_READ_UNRECORDED` when a check module
2363
+ reads a project file around the recorder; every committed example runs
2364
+ through it.
2365
+
2366
+ The check's waypoints come from the `@rig-c/parts` it loads, which no
2367
+ project file seals. `parts` is that package's `{name, version, integrity}`:
2368
+ the installed version and the integrity that `bun.lock` and
2369
+ `package-lock.json` beside the install record for it, each lockfile that
2370
+ exists. Only the parts entry is read, never a whole-lockfile hash, so an
2371
+ unrelated dependency bump leaves the closure valid. A missing or
2372
+ disagreeing entry refuses `JOB_RECORD_PARTS_LOCK_MISSING`. In the retained
2373
+ phase the reader compares the sealed identity with the host's parts, the
2374
+ ones the retained check loads, before it seals the closure files. A difference refuses
2375
+ `JOB_RECORD_PARTS_MISMATCH`, naming both versions; the remedy is to rebuild
2376
+ the project on the host's parts or to run the check where the sealed
2377
+ version is installed.
2378
+
2379
+ After its seals pass, a retained-root check refusal produces
2380
+ `JOB_RECORD_CLOSURE_INCOMPLETE`, carrying structured `details` containing
2381
+ only the check's finding codes and record paths. It fires only when the
2382
+ retained root lacks or changed a closure file. An option-set request for a
2383
+ question with no pending set refuses `JOB_RECORD_SUBJECT_INVALID` in both
2384
+ phases.
2385
+ Collection members bind their collection interview closure into a
2386
+ project-relative artifact with
2387
+ `rig-create collection-binding [<member dir>] [--out <path>]` (default
2388
+ `evidence/collection-binding.json`) and record `collection_binding` in
2389
+ `project.json` (`{ path, sha256, spec: "rig-create/collection-binding@1", collection_sha256 }`).
2390
+ The binding file seals the raw `../../collection.json` `sha256`, the full
2391
+ collection `interview` (`questions`, `framing`, `waypoints` and `post.items`,
2392
+ preserving `history[]` so `basis: "overridden"` checks verify
2393
+ `collection_answer_at` in isolated retained roots), and the member's resolved
2394
+ `inherited_answers`. A member without a valid, up-to-date `collection_binding`
2395
+ refuses with `JOB_RECORD_COLLECTION_EXTERNAL`.
2396
+
2397
+
2398
+ ## Parts-review receipt selection (create#267)
2399
+
2400
+ This section supersedes earlier saved-verdict command examples and evidence
2401
+ requirements in this document.
2402
+
2403
+ `signoff parts-review --draft <id>` and `structure-snapshot --draft <id>`
2404
+ select the latest parts-verdict receipt for that draft in `review_acts` ledger
2405
+ order. Both CLI-read and act receipts participate. The response retained by
2406
+ `parts-review` is hash-bound and validated through the same reader in the
2407
+ writers and check. The signoff retains its selected response binding and spec.
2408
+ A later response does not replace that historical binding. The existing latest
2409
+ changes-requested gate still applies. The removed `--evidence` and `--verdict`
2410
+ flags exit 2 with `PARTS_VERDICT_FILE_ROUTE_REMOVED` before writing.
2411
+
2412
+ Signatures before `PARTS_VERDICT_RECEIPT_SINCE` retain their legacy file
2413
+ bindings and WARN with `PARTS_VERDICT_FILE_LEGACY`. Check never opens their
2414
+ review-saved files. New signatures require a recorded API receipt.
2415
+
2416
+ The downstream review-package identity gate uses the selected draft's manifest
2417
+ and image bindings. Opaque CLI source paths cannot suppress this comparison.
2418
+ A mismatched accepted image retains `REVIEW_PACKAGE_PART_FILE_MISMATCH`.
2419
+ Historical saved-file approvals WARN and skip this unavailable comparison.
2420
+
2421
+ Historical response checks keep the existing `DRAFT_RIG_UNAVAILABLE` warning
2422
+ when ignored draft artifacts cannot be read. The retained API response remains
2423
+ hash- and spec-checked. New writers require the source bytes before recording.
2424
+ The examples query the pinned Review CLI, so a Review pin bump regenerates
2425
+ these responses and their dependent example bindings.
2426
+
2427
+ ## One artwork, one project, and the fix ledger (create#285)
2428
+
2429
+ One artwork is one project for its whole life. A new project is for a different
2430
+ painting or a different rig kind. A redo, a new source ladder, new seeds, a new
2431
+ tool version or a new parts route is a new version in the same project. Only
2432
+ recorded drafts and versions count as versions; any other build is an
2433
+ experiment.
2434
+
2435
+ `rig-create init <dir> --painting <file> --origin <words>` copies the painting
2436
+ into `inputs/`, registers it as the first input and declares its path in
2437
+ `project.painting`. It refuses with
2438
+ `INIT_ARTWORK_EXISTS` when a `project.json` directly under `--projects-root`
2439
+ (default: the new project's parent folder) has an input with the same sha256.
2440
+ `--same-artwork-reason <file>` allows it, and the new record keeps
2441
+ `same_artwork: { reason, at, projects }`. init without `--painting` is unchanged.
2442
+
2443
+ The fix ledger lives in `project.json.fix_ledger`, so a restart or a new build
2444
+ cannot drop an accepted fix unnoticed. `ledger add <id> --by <who> --check <check
2445
+ id> --bar <text> --note <ref> --verbatim-file <file>` records one owner fix:
2446
+ the note it answers (`corrections[<id>]` or `versions[v<n>].verdicts[<i>]`),
2447
+ the owner's words quoted from it, an opaque check id and the bar.
2448
+ `ledger retire <id> --by <who> --reason-file <file>` retires it with a reason.
2449
+ A retired row stays in the ledger.
2450
+
2451
+ The pixel checks are the project's own script. create binds its report and does
2452
+ not run or reproduce a check. A draft is recorded with
2453
+ `draft-rig ... --ledger-report <file>`, a `rig-create/fix-ledger-report@1`
2454
+ report:
2455
+
2456
+ ```
2457
+ {"schema": "rig-create/fix-ledger-report@1",
2458
+ "build": {"dir": "<as given>", "manifest_sha256": "<sha256 of the build's parts.json>"},
2459
+ "script_sha256": "<sha256 of the check script>",
2460
+ "rows": [{"id": "T1", "result": "pass|fail|unmeasurable", "measured": 3, "bar": "<text>", "reason": null}]}
2461
+ ```
2462
+
2463
+ The report stays under the project outside ignored roots, for example
2464
+ `evidence/fix-ledger/`. Build identity is `build.manifest_sha256` against the
2465
+ draft's bound `parts.json`; `build.dir` is not identity. Extra keys in a row
2466
+ are allowed. The draft records `ledger_report { path, sha256, spec }`,
2467
+ `fixes_against`, `fixes` and, when given, `known_regressions [{ id, reason }]`:
2468
+
2469
+ - The report must cover every live row (`DRAFT_LEDGER_INCOMPLETE`).
2470
+ - A row the previous draft passed must pass again. `fail`, `unmeasurable` or a
2471
+ missing row is a regression (`DRAFT_LEDGER_REGRESSION`), unless
2472
+ `--known-regression <row id>=<reason file>` names it. A known regression
2473
+ stays owed: the next draft passes that row or records it as a known
2474
+ regression again, with its own reason.
2475
+ - `fixes` lists the live rows this report passes that the last draft a person
2476
+ saw did not. That draft is the latest earlier one with a parts-review
2477
+ receipt, else the previous draft.
2478
+
2479
+ The filled example records the v1 bounce's lantern fix as row `T1` and a later
2480
+ draft, `lantern`, with a synthetic passing report
2481
+ (`templates/example-draft/evidence/fix-ledger/lantern.json`, written by
2482
+ `scripts/example-ledger.ts` through `generate-example-order.ts`).
2483
+
2484
+ `rig-create restart [<dir>] --by <human> --reason-file <file>` starts the record
2485
+ again at v1 when the owner orders it. It writes the record `init` writes, with
2486
+ no versions, drafts, signoffs, plan, answers or review acts. It keeps the name,
2487
+ title, `painting`, `same_artwork`, `inputs[]` and `fix_ledger` with every row, and appends
2488
+ `restarts[] { at, by, reason, previous_record_sha256 }`. create does not keep
2489
+ the earlier `project.json`; git history holds it, so restart refuses before it
2490
+ writes unless `project.json` is committed: tracked and byte-identical to its
2491
+ version at HEAD. create's writers don't commit, so commit `project.json`, then
2492
+ restart. Before writing, restart also re-validates every input with the input writer's checks. A kept part set
2493
+ (inputs under `<set>/parts/*.png` or `<set>/parts.json`) must keep its
2494
+ `parts.json`, its PNGs and its rig (`rig.json` and `mesh_report.json`). The
2495
+ first draft after a restart has no predecessor, but it covers the whole
2496
+ ledger, so the inherited fixes are checked on day one. A restarted record
2497
+ checks the same as a fresh `init` record: the same codes, which include the
2498
+ open interview's `INTERVIEW_INCOMPLETE` refusals, and 0 warnings. Codes are in
2499
+ [CHECKS.md § Fix ledger, ledger reports and restarts](CHECKS.md#fix-ledger-ledger-reports-and-restarts-create285).
2500
+
2501
+ ## Deriving the next moves: `rig-create next` (create#329)
2502
+
2503
+ `rig-create next [<project dir>] [--json]` is a read-only command that derives
2504
+ the project's next moves and whose they are from `project.json`:
2505
+
2506
+ - **Derived, never guessed.** Each item comes from a record field: `stage` and
2507
+ its exit requirement, open `plan.steps[]` and their `status`, lapsed
2508
+ approvals (`reopens[]` and `amendments[]` lapse lists), pending review jobs
2509
+ and `review_acts[]`, bound inputs and drafts, and the current `check`
2510
+ findings. Each item names the `field` it came from and what the record
2511
+ `awaits`. When required authored input is not yet in `project.json`, the item
2512
+ names the missing record field in `unanswered_by_record` and does not guess.
2513
+ - **Role ownership.** Every move names `role`:
2514
+ - `agent`: a complete, runnable `rig-create` command the agent may run now.
2515
+ - `producer`: a command of a pinned package (`rig-parts`, `rigc`).
2516
+ - `human`: a person's act collected through the review tool (`review_act`),
2517
+ with `command: null` and never a `--by user` writer.
2518
+ - **Refusals come first.** When `check` reports refusals (or publication holes),
2519
+ `next` lists each in `refusals[]` with its `CHECKS.md` remedy (`fix`) before
2520
+ any forward move. A project with a blocking refusal has `moves: []` and
2521
+ `awaits: []`.
2522
+ - **Rigging plan before sign-off.** At `rigging-interview`, when no rig step
2523
+ belongs to that approval stage, `next` lists `plan add <id> <dir> --by agent
2524
+ --phase rig …` for each answered guide without a step (bones, meshes,
2525
+ constraints, motion). The agent supplies the authored plan fields; these
2526
+ steps must be approved at `rigging-interview`. The human sign-off stays
2527
+ unanswered by record until the plan steps exist.
2528
+ - **Read-only and deterministic.** `project.json` and the project tree remain
2529
+ byte-identical. An `accepted` project with no lapsed approvals reports
2530
+ `accepted: true`, `moves: []` and exits `0`.
2531
+
2532
+ `--json` emits a `rig-create/next@1` document:
2533
+
2534
+ ```json
2535
+ {
2536
+ "schema": "rig-create/next@1",
2537
+ "project_id": "<project.name>",
2538
+ "stage": "<project.stage>",
2539
+ "effective_stage": "<check effective_stage>",
2540
+ "accepted": false,
2541
+ "refusals": [
2542
+ {
2543
+ "code": "<CHECK_CODE>",
2544
+ "field": "<record path>",
2545
+ "message": "<finding message>",
2546
+ "fix": "<CHECKS.md remedy>"
2547
+ }
2548
+ ],
2549
+ "awaits": ["<awaited record target>"],
2550
+ "moves": [
2551
+ {
2552
+ "role": "agent|producer|human",
2553
+ "field": "<source record field>",
2554
+ "awaits": "<awaited record target>",
2555
+ "command": "<complete runnable command or null>",
2556
+ "act": "<description of the move or review-tool act>",
2557
+ "review_act": {
2558
+ "kind": "parts-verdict|interview-picks|review-verdict|signoff|signoff-checkpoint",
2559
+ "stage": "<stage>"
2560
+ },
2561
+ "lapse": {
2562
+ "code": "REOPEN_SIGNOFF_LAPSED|REOPEN_VERDICT_LAPSED|AMENDED_STEP",
2563
+ "source_field": "reopens[0]",
2564
+ "target_field": "signoffs[2]",
2565
+ "message": "<warning message>"
2566
+ },
2567
+ "unanswered_by_record": "<missing record field when not derivable>"
2568
+ }
2569
+ ]
2570
+ }
2571
+ ```
2572
+
2573
+
2574
+ ### Operator withdrawal of an execution checkpoint
2575
+
2576
+ `plan checkpoint-withdraw <id> [<project dir>] --by <operator>
2577
+ --reason-file <file> --quote-file <file>` appends one optional
2578
+ `plan.checkpoint_withdrawals[]` entry. Each entry has schema
2579
+ `rig-create/checkpoint-withdrawal@1`, the complete original `step`, its `sha256`
2580
+ from stepHash, its per-id replacement `generation`, `by`, `at`, `reason` and
2581
+ `verbatim`. Both input files are strict UTF-8 and retain their exact text.
2582
+ The operation requires an enabled unfinished checkpoint in the current execution
2583
+ stage. Identical retry inputs preserve project bytes. Conflicting retries refuse.
2584
+
2585
+ The original step, checkpoint decision, proposal, approval hashes and signatures
2586
+ remain unchanged. A withdrawal supplies operator policy without creating a human
2587
+ act or signature. Only an exact step hash and replacement generation match makes
2588
+ the effective requirement false. A later revision or re-enabled decision needs
2589
+ its own policy. Old journal entries and event-time status snapshots remain
2590
+ history. Absence of the optional journal preserves existing behavior.
2591
+
2592
+
2593
+ ### Operator amendment of an execution bar
2594
+
2595
+ `plan bar-amend <id> [<project dir>] --by <operator> --reason-file <file>
2596
+ --quote-file <file> --bar-file <file>` appends an optional
2597
+ `plan.bar_amendments[]` entry with exactly these fields:
2598
+
2599
+ ```json
2600
+ {
2601
+ "schema": "rig-create/bar-amendment@1",
2602
+ "step": { "id": "geometry", "phase": "rig", "bar": "Original source-bound bar.", "status": "pending" },
2603
+ "sha256": "<original stepHash>",
2604
+ "generation": 0,
2605
+ "amended_bar": "<exact bar-file text>",
2606
+ "by": "<operator>",
2607
+ "at": "<ISO timestamp>",
2608
+ "reason": "<exact reason-file text>",
2609
+ "verbatim": "<exact quote-file text>"
2610
+ }
2611
+ ```
2612
+
2613
+ `step` retains the complete original object; the shape above abbreviates its fields.
2614
+ `generation` counts retained replacements for that step id. The amendment applies
2615
+ only to the exact current original hash and generation. A later revision does
2616
+ not inherit it, even when the original text is restored. Historical amendments
2617
+ remain bound to their retained original version. A malformed journal grants no
2618
+ partial policy and refuses check; a future entry is invalid at check's instant.
2619
+ Absence of this optional journal preserves existing behavior.
2620
+
2621
+ This operation requires one unfinished step in its current execution stage and
2622
+ nonempty strict UTF-8 input files. The files retain BOMs, whitespace and final
2623
+ newlines. Their contents are checked before and within the bound-job and plan
2624
+ locks and again before commit. Identical retry inputs preserve project bytes,
2625
+ inode and modification times; changed inputs for an already-bound amendment
2626
+ refuse. The original step, every approval hash and signature, checkpoint policy,
2627
+ and historical act receipt remain unchanged. Normal signed-step revision guards
2628
+ still apply.
2629
+
2630
+ The operator records an explicitly authorized quoted policy here, without a new
2631
+ human act or signature. This authority is specific to bar amendment; it does not
2632
+ permit other human decisions outside Review. The effective bar supplies the
2633
+ existing warning-only appearance wording lint. Create does not interpret English
2634
+ bar text as an automatic producer measurement or proof of completion. Normal
2635
+ done, next and advance retain their existing checks and producer-report contracts.
2636
+
2637
+ ## Updating a project's release pins
2638
+
2639
+ Run `rig-create packages upgrade [dir] --by <operator>` before opening a record
2640
+ ask on a host whose parts release differs from the project's. The writer moves
2641
+ `dependencies` for core, parts and review to Create's release pins, using a
2642
+ staged `bun install --ignore-scripts`. No manual `bun add` is needed. The
2643
+ record publication contract compares parts only; core and review still move
2644
+ with Create's complete release pin set. Missing pins are recorded as `null`.
2645
+
2646
+ The writer appends `package_upgrades[]`, with `by`, `at`, `packages` (each entry
2647
+ has `name`, `from`, `to`) and `lock_sha256` (the installed lockfile's sha256).
2648
+ It preserves literal manifest and record bytes outside those fields. The
2649
+ lockfile and install are regenerated; lifecycle scripts are not executed.
2650
+ It never downgrades, and refuses an already-current manifest. Local and
2651
+ workspace dependencies are refused because staging would change their base.
2652
+
2653
+ The same project check runs in fresh processes before and after the install
2654
+ swap, avoiding cached parts modules. Added or changed refusals abort the move;
2655
+ the record, manifest, lockfile and original install are restored. Complete
2656
+ this command before another process reads or writes the project. This is an
2657
+ operator pin migration, not a review verdict or stage transition.