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,301 @@
1
+ import { fixedNotApplicableCamera } from './direction-citation.ts';
2
+ /**
3
+ * Provenance per config section (docs/INTERVIEW.md § Provenance): every
4
+ * section of the config a version was built from cites the guide sentence or
5
+ * the delegation it rests on, in `versions[].provenance`. Run as part of
6
+ * `interview --check`, over the same `Ctx`; the codes are in docs/CHECKS.md
7
+ * § Provenance.
8
+ *
9
+ * Which sections a config has:
10
+ * - The config file named in `provenance.config.path` is read when it is under
11
+ * the project folder and its sha256 matches `provenance.config.sha256`; its
12
+ * top-level keys are the sections, and each must be cited. A rig config's
13
+ * records (`x-…`) and annotations (`note`, `…_note`) are not sections.
14
+ * - Otherwise (`derived/` is not kept, so this is the usual case) the sections
15
+ * `provenance.sections` lists are the ones checked, and the report says the
16
+ * config's own list could not be verified (`provenance[].config`).
17
+ *
18
+ * Which sections need a citation:
19
+ * - `kind: "rig"`: the config is a `@rig-c/parts` config; a section placed
20
+ * `none` in src/waypoints.ts (`key`, `note`, `generation`, …) is not a
21
+ * direction call and needs none. Every other section does.
22
+ * - `kind: "post"`: the config is the post tool's, whose sections are not
23
+ * parts sections and are not on the placement table; every one of them is a
24
+ * direction call and needs a citation, and it cites a post item (or a
25
+ * framing answer), never a rig waypoint.
26
+ */
27
+ import { createHash } from 'node:crypto';
28
+ import { existsSync, readFileSync } from './check-reads.ts';
29
+ import { relative, resolve, sep } from 'node:path';
30
+
31
+ import { answered, asArray, asString, collectionQuestion, isDecision, isObject, itemFor, type Ctx, type Item } from './check.ts';
32
+ import type { PartsSections } from './parts-sections.ts';
33
+ import type { Json, JsonObject } from './templates.ts';
34
+ import { SECTION_PLACEMENT, WAYPOINT_ASKS } from './waypoints.ts';
35
+
36
+ /** How the version's section list was obtained. */
37
+ export type ConfigRead =
38
+ | 'verified' // the config file was read and its hash matched; its keys are the sections
39
+ | 'absent' // no file at provenance.config.path under the project; the listed sections were checked
40
+ | 'changed' // the file is there but its hash differs; the listed sections were checked
41
+ | 'unreadable'; // the file is there but is not a JSON object, or the path leaves the project
42
+
43
+ export interface ProvenanceSummary {
44
+ readonly version: string;
45
+ readonly kind: 'rig' | 'post';
46
+ readonly config_path: string | null;
47
+ readonly config: ConfigRead;
48
+ /** The sections checked: the config's keys when verified, else the ones provenance lists. */
49
+ readonly sections: string[];
50
+ }
51
+
52
+ function versionLabel(v: JsonObject, index: number): string {
53
+ return typeof v.n === 'number' ? `versions[v${v.n}]` : `versions[${index}]`;
54
+ }
55
+
56
+ function sha256(file: string): string | null {
57
+ try {
58
+ return createHash('sha256').update(readFileSync(file)).digest('hex');
59
+ } catch {
60
+ return null; // a directory or an unreadable file: reported as `unreadable`, never a crash
61
+ }
62
+ }
63
+
64
+ /**
65
+ * A top-level key parts reads nothing from: a record (`x-` and at least one
66
+ * character more, any JSON) or an annotation (`note` or `<name>_note`, holding
67
+ * a string). Records are tested first, so `x-seed_note` is a record whatever it
68
+ * holds. This mirrors `isRecordKey` and `isDoorKey` with the loader's string
69
+ * test for annotations in @rig-c/parts `src/config.ts` (AUTHORING §3); `xstatus`,
70
+ * `x_status`, `X-status` and a bare `x-` are not record names.
71
+ */
72
+ function isAnnotationOrRecord(key: string, value: unknown): boolean {
73
+ if (key.length > 2 && key.startsWith('x-')) return true;
74
+ return (key === 'note' || key.endsWith('_note')) && typeof value === 'string';
75
+ }
76
+
77
+ /**
78
+ * Reads the config's top-level keys when the file is the one the version was
79
+ * built from, leaving out the keys `skip` is true for (a rig config's records
80
+ * and annotations).
81
+ */
82
+ function readConfigSections(
83
+ dir: string,
84
+ config: Json | undefined,
85
+ skip: (key: string, value: unknown) => boolean = () => false,
86
+ ): { read: ConfigRead; path: string | null; sections: string[] | null; document?: JsonObject } {
87
+ const path = isObject(config) ? asString(config.path) : null;
88
+ if (path === null) return { read: 'absent', path: null, sections: null };
89
+ const file = resolve(dir, path);
90
+ const rel = relative(dir, file);
91
+ if (rel === '' || rel.startsWith('..') || rel.startsWith(sep)) return { read: 'unreadable', path, sections: null };
92
+ if (!existsSync(file)) return { read: 'absent', path, sections: null };
93
+ const expected = isObject(config) ? asString(config.sha256) : null;
94
+ const actual = sha256(file);
95
+ if (actual === null) return { read: 'unreadable', path, sections: null };
96
+ if (expected === null || actual !== expected) return { read: 'changed', path, sections: null };
97
+ let parsed: unknown;
98
+ try {
99
+ parsed = JSON.parse(readFileSync(file, 'utf8'));
100
+ } catch {
101
+ return { read: 'unreadable', path, sections: null };
102
+ }
103
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return { read: 'unreadable', path, sections: null };
104
+ return { read: 'verified', path, document: parsed as JsonObject, sections: Object.entries(parsed).filter(([k, v]) => !skip(k, v)).map(([k]) => k) };
105
+ }
106
+
107
+ /**
108
+ * The answers a citation may rest on: the current one, and every earlier one
109
+ * in the question's `history[]` — a version built before an answer was
110
+ * replaced was built from the answer as it was then (INTERVIEW.md § Answer
111
+ * shape: a replaced answer moves whole into `history[]`).
112
+ */
113
+ export function answersOf(item: Item, ctx: Pick<Ctx, 'collection'>): JsonObject[] {
114
+ const s = item.state;
115
+ const current = s.state === 'user' || s.state === 'delegated' || s.state === 'not_applicable' ? [s.answer] : [];
116
+ const history = asArray(item.record.history).filter(isObject);
117
+ // An inherited answer's earlier versions live in the collection question's history, not the member's.
118
+ if (isObject(item.raw) && item.raw.basis === 'inherited' && ctx.collection !== null && isObject(item.raw.from)) {
119
+ const question = asString(item.raw.from.question);
120
+ const source = question === null ? undefined : collectionQuestion(ctx.collection, question);
121
+ if (source !== undefined) history.push(...asArray(source.history).filter(isObject));
122
+ }
123
+ return [...current, ...history];
124
+ }
125
+
126
+ /** The stored sentences a quote may be taken from: each answer's `verbatim` and every `additions[].verbatim`. */
127
+ function sentences(item: Item, ctx: Pick<Ctx, 'collection'>): string[] {
128
+ const out: string[] = [];
129
+ for (const answer of answersOf(item, ctx)) {
130
+ const verbatim = asString(answer.verbatim);
131
+ if (verbatim !== null) out.push(verbatim);
132
+ for (const add of asArray(answer.additions)) if (isObject(add) && asString(add.verbatim) !== null) out.push(add.verbatim as string);
133
+ }
134
+ return out;
135
+ }
136
+
137
+ /** A delegation the citation can rest on: an answer that is delegated, or a user answer that left part open, with the agent's decision written. */
138
+ function delegationOf(item: Item, ctx: Pick<Ctx, 'collection'>): 'ok' | 'not-delegated' | 'undecided' {
139
+ let undecided = false;
140
+ for (const answer of answersOf(item, ctx)) {
141
+ if (answer.delegated === true) {
142
+ if (isDecision(answer.agent_decision)) return 'ok';
143
+ undecided = true;
144
+ } else if (answer.delegated === false && isDecision(answer.agent_decision)) {
145
+ return 'ok';
146
+ }
147
+ }
148
+ return undecided ? 'undecided' : 'not-delegated';
149
+ }
150
+
151
+ /** Answers that explicitly record this canonical ask. Keep each answer intact for evidence checks. */
152
+ export function askAnswers(item: Item, ctx: Pick<Ctx, 'collection'>, id: string): JsonObject[] {
153
+ return answersOf(item, ctx).filter((answer) => asArray(answer.asks).some((entry) => isObject(entry) && entry.id === id));
154
+ }
155
+
156
+ function checkCitationAsks(ctx: Ctx, item: Item, raw: JsonObject, path: string): void {
157
+ const answers = answersOf(item, ctx);
158
+ if (!('asks' in raw)) {
159
+ if (answers.some((answer) => Array.isArray(answer.asks))) {
160
+ ctx.warnings.push({ code: 'CITATION_ASKS_MISSING', path, message: `${path}: guide '${item.id}' has per-ask coverage; add asks naming the recorded ask ids this citation rests on` });
161
+ }
162
+ return;
163
+ }
164
+ if (!Array.isArray(raw.asks) || raw.asks.length === 0 || raw.asks.some((id) => typeof id !== 'string' || id === '')
165
+ || new Set(raw.asks).size !== raw.asks.length) {
166
+ ctx.refusals.push({ code: 'CITATION_ASKS_MALFORMED', path, message: `${path}: guide '${item.id}' needs asks as a nonempty list of unique ask ids; name each recorded ask once` });
167
+ return;
168
+ }
169
+ for (const id of raw.asks as string[]) {
170
+ if (!(item.kind !== 'waypoint' && id === item.id) && !WAYPOINT_ASKS[item.id]?.some((ask) => ask.id === id)) {
171
+ ctx.refusals.push({ code: 'CITATION_ASK_UNKNOWN', path, message: `${path}: guide '${item.id}' has no ask '${id}'; choose an id from its waypoint asks` });
172
+ continue;
173
+ }
174
+ const recorded = askAnswers(item, ctx, id);
175
+ if (recorded.length === 0) {
176
+ ctx.refusals.push({ code: 'CITATION_ASK_UNRECORDED', path, message: `${path}: guide '${item.id}' does not record ask '${id}'; record its user answer or delegation before citing it` });
177
+ continue;
178
+ }
179
+ const quote = asString(raw.quote);
180
+ const valid = recorded.some((answer) => asArray(answer.asks).some((entry) => {
181
+ if (!isObject(entry) || entry.id !== id) return false;
182
+ if (entry.state === 'delegated') return raw.delegated === true && isDecision(answer.agent_decision);
183
+ if (entry.state !== 'user' || raw.delegated === true || quote === null || quote === '') return false;
184
+ return [answer.verbatim, ...asArray(answer.additions).filter(isObject).map((add) => add.verbatim)]
185
+ .some((sentence) => typeof sentence === 'string' && sentence.includes(quote));
186
+ }));
187
+ if (!valid) ctx.refusals.push({ code: 'CITATION_ASK_STATE', path, message: `${path}: guide '${item.id}' ask '${id}' contradicts this citation; quote the user words for a user ask, or use delegated: true with a decided delegation on the same answer` });
188
+ }
189
+ }
190
+
191
+ function needsCitation(kind: 'rig' | 'post', section: string): boolean {
192
+ if (kind === 'post') return true;
193
+ return SECTION_PLACEMENT[section]?.kind !== 'none';
194
+ }
195
+
196
+ export function checkEntry(ctx: Ctx, kind: 'rig' | 'post', path: string, raw: Json): void {
197
+ if (!isObject(raw)) {
198
+ ctx.refusals.push({ code: 'ENTRY_UNCITED', path, message: `${path}: an entry is an object { entry, guide, quote | delegated: true }` });
199
+ return;
200
+ }
201
+ const label = asString(raw.entry);
202
+ const named = label === null ? path : `${path} ('${label}')`;
203
+ const quote = asString(raw.quote);
204
+ const delegated = raw.delegated === true;
205
+ const guide = asString(raw.guide);
206
+ if ((quote === null || quote === '') && !delegated) {
207
+ ctx.refusals.push({ code: 'ENTRY_UNCITED', path, message: `${named}: neither a quote nor delegated: true; a decision nobody gave — find the sentence, ask for one, or remove the entry` });
208
+ return;
209
+ }
210
+ if (guide === null) {
211
+ ctx.refusals.push({ code: 'CITATION_GUIDE_UNKNOWN', path, message: `${named}: no guide id; name the answer it rests on` });
212
+ return;
213
+ }
214
+ const item = itemFor(ctx, guide);
215
+ if (item === undefined) {
216
+ ctx.refusals.push({ code: 'CITATION_GUIDE_UNKNOWN', path, message: `${named}: cites '${guide}', which is no framing, waypoint or post item of this interview` });
217
+ return;
218
+ }
219
+ if (!answered(item.state)) {
220
+ ctx.refusals.push({ code: 'CITATION_GUIDE_SILENT', path, message: `${named}: cites '${guide}', which has no answer (${item.path} is ${item.state.state})` });
221
+ return;
222
+ }
223
+ if (kind === 'post' && item.kind === 'waypoint') {
224
+ ctx.refusals.push({ code: 'CITATION_WRONG_PHASE', path, message: `${named}: a post version cites rig waypoint '${guide}'; a post config section rests on a post item (or a framing answer) — rig feedback makes a new rig version` });
225
+ return;
226
+ }
227
+ checkCitationAsks(ctx, item, raw, path);
228
+ if (quote !== null && quote !== '') {
229
+ if (!sentences(item, ctx).some((s) => s.includes(quote))) {
230
+ ctx.refusals.push({ code: 'QUOTE_NOT_FOUND', path, message: `${named}: quote ${JSON.stringify(quote)} is in no verbatim or additions[] of ${item.path} (current or history[]); a quote is an exact fragment of a stored sentence` });
231
+ }
232
+ } else {
233
+ const d = delegationOf(item, ctx);
234
+ if (d === 'not-delegated') {
235
+ ctx.refusals.push({ code: 'DELEGATION_NOT_GIVEN', path, message: `${named}: cites '${guide}' as delegated, but no answer at ${item.path} (current or history[]) is a delegation or carries an agent_decision — the user did not hand this over` });
236
+ } else if (d === 'undecided') {
237
+ ctx.refusals.push({ code: 'DELEGATION_NOT_GIVEN', path, message: `${named}: cites the delegation at '${guide}', but ${item.path} has no agent_decision; the decision the section follows is not written` });
238
+ }
239
+ }
240
+ }
241
+
242
+ /** Checks every `versions[]` entry of kind `rig` or `post`; returns what was read, per version, for the report. */
243
+ export function checkProvenance(ctx: Ctx, parts: PartsSections | null): ProvenanceSummary[] {
244
+ const summaries: ProvenanceSummary[] = [];
245
+ const known = new Set([...Object.keys(SECTION_PLACEMENT), ...(parts?.sections ?? [])]);
246
+ asArray(ctx.project.versions).forEach((v, index) => {
247
+ if (!isObject(v) || v.pre_interview === true || (v.kind !== 'rig' && v.kind !== 'post')) return;
248
+ const kind = v.kind;
249
+ const label = versionLabel(v, index);
250
+ const prov = v.provenance;
251
+ if (!isObject(prov) || !isObject(prov.sections)) {
252
+ ctx.refusals.push({ code: 'PROVENANCE_MISSING', path: `${label}.provenance`, message: `${label}.provenance has no sections{}; a version built from a config cites, per section, the guide it rests on` });
253
+ return;
254
+ }
255
+ const cited = prov.sections;
256
+ // A rig config's records and annotations are not sections and need no citation; a section parts or the table knows is kept.
257
+ const read = readConfigSections(ctx.dir, prov.config, kind === 'rig' ? (k, val) => !known.has(k) && isAnnotationOrRecord(k, val) : undefined);
258
+ const sections = kind === 'post' && isObject(prov.direction) ? ['camera', 'timing'] : read.sections ?? Object.keys(cited);
259
+ summaries.push({ version: label, kind, config_path: read.path, config: read.read, sections });
260
+ if (read.read === 'changed') {
261
+ ctx.warnings.push({ code: 'PROVENANCE_CONFIG_CHANGED', path: `${label}.provenance.config`, message: `${label}.provenance.config: ${read.path} is present but its sha256 is not the recorded one; the sections listed were checked, not the file's` });
262
+ }
263
+ for (const section of sections) {
264
+ const path = `${label}.provenance.sections[${section}]`;
265
+ if (kind === 'rig' && !known.has(section)) {
266
+ ctx.refusals.push({ code: 'SECTION_UNKNOWN', path, message: `${path}: '${section}' is no @rig-c/parts${parts === null ? '' : ` ${parts.version}`} section; a misspelt name leaves the real section uncited` });
267
+ continue;
268
+ }
269
+ const entries = asArray(cited[section]);
270
+ if (entries.length === 0) {
271
+ if (needsCitation(kind, section)) {
272
+ ctx.refusals.push({ code: 'SECTION_UNCITED', path, message: `${path}: the config has section '${section}' and nothing cites the guide or delegation it rests on` });
273
+ }
274
+ continue;
275
+ }
276
+ entries.forEach((e, i) => {
277
+ const item = isObject(e) && typeof e.guide === 'string' ? itemFor(ctx, e.guide) : undefined;
278
+ if (kind === 'post' && isObject(prov.direction) && fixedNotApplicableCamera(section, item, e, read.document)) return;
279
+ checkEntry(ctx, kind, `${path}[${i}]`, e);
280
+ });
281
+ }
282
+ // A citation under a key the config listing skipped (a record or annotation) is still no section.
283
+ if (kind === 'rig') {
284
+ for (const section of Object.keys(cited)) {
285
+ if (known.has(section) || sections.includes(section)) continue;
286
+ const path = `${label}.provenance.sections[${section}]`;
287
+ ctx.refusals.push({ code: 'SECTION_UNKNOWN', path, message: `${path}: '${section}' is no @rig-c/parts${parts === null ? '' : ` ${parts.version}`} section; a misspelt name leaves the real section uncited` });
288
+ }
289
+ }
290
+ });
291
+ return summaries;
292
+ }
293
+
294
+ /** Lines for the human report: one per version whose config could not be read, so an unverified section list is said, not implied. */
295
+ export function formatProvenance(summaries: readonly ProvenanceSummary[]): string[] {
296
+ const unverified = summaries.filter((s) => s.config !== 'verified');
297
+ if (summaries.length === 0) return [];
298
+ const head = ` provenance: ${summaries.length} version${summaries.length === 1 ? '' : 's'} checked`;
299
+ if (unverified.length === 0) return [`${head}; every config read`];
300
+ return [`${head}; config not read for ${unverified.map((s) => `${s.version} (${s.config})`).join(', ')} — the sections provenance lists were checked`];
301
+ }
@@ -0,0 +1,26 @@
1
+ /** Resolve only the post approved in a sealed decision record, never a UI selector. */
2
+ import { asArray, isObject } from './check.ts';
3
+ import { collectReopens, liveSignoff } from './reopen.ts';
4
+ import { acceptedReviewVerdict } from './review.ts';
5
+ import { refuse } from './refusal.ts';
6
+ import type { JsonObject } from './templates.ts';
7
+
8
+ export function recordDirectionTarget(project: JsonObject, at = Infinity): number | null {
9
+ const reopens = collectReopens(project, []);
10
+ const posts = asArray(project.versions).filter(isObject).filter(version => version.kind === 'post'
11
+ && Number.isSafeInteger(version.n) && Number(version.n) > 0
12
+ && Number.isFinite(Date.parse(String(version.at))) && Date.parse(String(version.at)) <= at
13
+ && liveSignoff(reopens, 'post', Date.parse(String(version.at))));
14
+ const current = posts.at(-1);
15
+ if (!current || asArray(project.versions).filter(isObject).filter(version => version.n === current.n).length !== 1) return null;
16
+ const index = acceptedReviewVerdict(current, 'direction-review');
17
+ const verdict = index >= 0 ? asArray(current.verdicts)[index] : undefined;
18
+ return isObject(verdict) && Number.isFinite(Date.parse(String(verdict.at))) && Date.parse(String(verdict.at)) <= at
19
+ && liveSignoff(reopens, 'direction-review', Date.parse(String(verdict.at))) ? Number(current.n) : null;
20
+ }
21
+
22
+ export function requireRecordDirectionTarget(project: JsonObject, at = Infinity): number {
23
+ const version = recordDirectionTarget(project, at);
24
+ if (version === null) refuse('REVIEW_ACT_RECORD_TARGET_INVALID', 'project.json.versions: the frozen record does not bind one current live post with an accepted direction-review verdict; resolve ambiguous or stale verdicts through the writers and publish the record again');
25
+ return version;
26
+ }
@@ -0,0 +1,18 @@
1
+ import { readFileSync } from './check-reads.ts';
2
+
3
+ import { refuse, USAGE_EXIT } from './refusal.ts';
4
+
5
+ /** Decode valid UTF-8 without removing a BOM, spaces, CRLFs or the final newline. */
6
+ export function readVerbatim(file: string): string {
7
+ let bytes: Buffer;
8
+ try {
9
+ bytes = readFileSync(file === '-' ? 0 : file);
10
+ } catch (error) {
11
+ refuse('VERBATIM_UNREADABLE', `${file}: ${error instanceof Error ? error.message : String(error)}`);
12
+ }
13
+ try {
14
+ return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
15
+ } catch {
16
+ refuse('VERBATIM_INVALID_UTF8', 'verbatim input must be valid UTF-8 to round-trip through project.json', USAGE_EXIT);
17
+ }
18
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The refusals a record already carries (docs/PROJECT.md § Refusals a record already carries).
3
+ *
4
+ * A writer's baseline is `checkProject` on the record as read, at write time. It is never stored
5
+ * in the record. A candidate refusal is new when the baseline does not cover it: findings match
6
+ * by code and path, counting multiplicity, so the same code at a different path is new. Removing
7
+ * a carried refusal is always allowed. A check that stopped early (an unreadable record, schema or
8
+ * stage) validated nothing after that point, so it supplies no baseline to any writer: every writer
9
+ * takes its baseline through `baselineOf` or `carriedRefusals`, and a stored pending-answer baseline
10
+ * does not stand in for a stopped-early check. `advance` does not use this: it still needs 0 refusals.
11
+ */
12
+ import { checkProject, type CheckReport, type Finding } from './check.ts';
13
+ import type { JsonObject } from './templates.ts';
14
+
15
+ type CheckOptions = Parameters<typeof checkProject>[2];
16
+
17
+ export const findingKey = (finding: Pick<Finding, 'code' | 'path'>): string => JSON.stringify([finding.code, finding.path]);
18
+
19
+ /** Count findings by code and path. */
20
+ export function countFindings(findings: Iterable<Pick<Finding, 'code' | 'path'>>): Map<string, number> {
21
+ const counts = new Map<string, number>();
22
+ for (const finding of findings) counts.set(findingKey(finding), (counts.get(findingKey(finding)) ?? 0) + 1);
23
+ return counts;
24
+ }
25
+
26
+ /** A check stops early on an unreadable record, an unknown schema or an unknown stage, and reads nothing after it. */
27
+ export function stoppedEarly(report: CheckReport): boolean {
28
+ return report.stage === null;
29
+ }
30
+
31
+ /** The baseline a check report supplies: its refusals counted by code and path, or none when the check stopped early. */
32
+ export function baselineOf(report: CheckReport): Map<string, number> {
33
+ return stoppedEarly(report) ? new Map() : countFindings(report.refusals);
34
+ }
35
+
36
+ /** The baseline of a record as read, at write time. */
37
+ export function carriedRefusals(dir: string, before: JsonObject, options: CheckOptions): Map<string, number> {
38
+ return baselineOf(checkProject(dir, structuredClone(before), options));
39
+ }
40
+
41
+ /** The findings in `after` that `baseline` does not cover, in check order. The baseline is not changed. */
42
+ export function newFindings(baseline: ReadonlyMap<string, number>, after: readonly Finding[]): Finding[] {
43
+ const left = new Map(baseline);
44
+ return after.filter(finding => {
45
+ const count = left.get(findingKey(finding)) ?? 0;
46
+ left.set(findingKey(finding), count - 1);
47
+ return count <= 0;
48
+ });
49
+ }
50
+
51
+ /** The candidate's check refusals that the record as read did not already carry. */
52
+ export function newRefusals(dir: string, before: JsonObject, candidate: JsonObject, options: CheckOptions): Finding[] {
53
+ const baseline = carriedRefusals(dir, before, options);
54
+ return newFindings(baseline, checkProject(dir, candidate, options).refusals);
55
+ }
package/src/refusal.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A named refusal: a stable code a caller (or a test) can match on, a message
3
+ * naming the object refused, and the exit code the CLI leaves with.
4
+ *
5
+ * Every command refuses through this, so `interview --check` and later
6
+ * commands print and exit the same way `init` does.
7
+ */
8
+ export class Refusal extends Error {
9
+ readonly code: string;
10
+ readonly exitCode: number;
11
+
12
+ constructor(code: string, message: string, exitCode = 1) {
13
+ super(message);
14
+ this.name = 'Refusal';
15
+ this.code = code;
16
+ this.exitCode = exitCode;
17
+ }
18
+ }
19
+
20
+ export function refuse(code: string, message: string, exitCode = 1): never {
21
+ throw new Refusal(code, message, exitCode);
22
+ }
23
+
24
+ /** The one-line form printed to stderr: `rig-create: CODE: message`. */
25
+ export function formatRefusal(refusal: Refusal): string {
26
+ return `rig-create: ${refusal.code}: ${refusal.message}`;
27
+ }
28
+
29
+ /** Exit code for a usage error (unknown command, unknown option, missing argument). */
30
+ export const USAGE_EXIT = 2;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The approvals a reopen voided and nothing has restored yet (rig-c/create#220). A reopen is
3
+ * legitimate, so each one is a WARN naming the record, the reopen and the act that restores it.
4
+ * Liveness is the same `liveSignoff` and rig-version `live` the gates read; this module only
5
+ * names which reopen did it. Amendment lapses are listed by the report's `amendments[].lapsed`.
6
+ */
7
+ import { asArray, collectSignoffs, isObject, type Finding } from './check.ts';
8
+ import { collectReopens, liveSignoff, reopenIndex, type Reopen } from './reopen.ts';
9
+ import { acceptedReviewVerdict, latestAccepted, readVersions } from './review.ts';
10
+ import { PARTS_REVIEW, RIG_REVIEW, stageIndex } from './stages.ts';
11
+ import type { JsonObject } from './templates.ts';
12
+
13
+ const DIRECTION_REVIEW = 'direction-review';
14
+
15
+ /** The earliest reopen (amendments excluded) that voids an approval of `stage` made at `at`. */
16
+ function voidingReopen(project: JsonObject, events: readonly Reopen[], stage: string, at: number): string | null {
17
+ const cutoff = events.filter(e => !('index' in e) && stageIndex(e.stage) <= stageIndex(stage) && !(at > e.at))
18
+ .reduce<Reopen | null>((a, e) => (a === null || e.at < a.at ? e : a), null);
19
+ return cutoff === null ? null
20
+ : `the reopen to ${cutoff.stage} at ${new Date(cutoff.at).toISOString()} (reopens[${reopenIndex(project, cutoff)}])`;
21
+ }
22
+
23
+ function signoffRemedy(stage: string, checkpoint: string | null): string {
24
+ if (checkpoint !== null) return `ask the person to approve checkpoint ${checkpoint} again in the review tool and record it with rig-create signoff ${stage} --checkpoint ${checkpoint}`;
25
+ if (stage === PARTS_REVIEW) return 'ask the person to review the parts again in the review tool and record the verdict with rig-create parts-review --draft <id> --from-review-cli followed by signoff parts-review --draft <id> --diff-report <report>';
26
+ if (stage === RIG_REVIEW || stage === DIRECTION_REVIEW) return `ask the person to accept a live version again at ${stage} in the review tool, record it with rig-create review <n> --verdict accepted --review ${stage}, and sign with rig-create signoff ${stage}`;
27
+ return `ask the person to sign ${stage} again in the review tool and record it with rig-create signoff ${stage}`;
28
+ }
29
+
30
+ /** One WARN per lapsed approval (the latest record of each), until a live approval of the same kind exists. */
31
+ export function reopenLapseWarnings(project: JsonObject): Finding[] {
32
+ const events = collectReopens(project, []);
33
+ if (!events.some(e => !('index' in e))) return [];
34
+ const out: Finding[] = [];
35
+ const latest = new Map<string, { index: number; stage: string; checkpoint: string | null; by: string }>();
36
+ const restored = new Set<string>();
37
+ for (const s of collectSignoffs(project, [])) {
38
+ if (stageIndex(s.stage) < 0) continue;
39
+ const key = `${s.stage}\u0000${s.checkpoint ?? ''}`;
40
+ if (liveSignoff(events, s.stage, s.at)) {
41
+ restored.add(key);
42
+ continue;
43
+ }
44
+ const by = voidingReopen(project, events, s.stage, s.at);
45
+ if (by !== null) latest.set(key, { index: s.index, stage: s.stage, checkpoint: s.checkpoint, by });
46
+ }
47
+ for (const [key, s] of latest) {
48
+ if (restored.has(key)) continue;
49
+ const path = `signoffs[${s.index}]`;
50
+ const what = s.checkpoint === null ? `the ${s.stage} sign-off` : `the ${s.stage} checkpoint ${s.checkpoint} sign-off`;
51
+ out.push({ code: 'REOPEN_SIGNOFF_LAPSED', path, message: `${path}: ${what} was voided by ${s.by}; ${signoffRemedy(s.stage, s.checkpoint)}` });
52
+ }
53
+
54
+ const versions = readVersions(project);
55
+ if (latestAccepted(versions) === null) {
56
+ const rig = versions.rigs.filter(r => !r.live && r.accepted !== null).reduce<(typeof versions.rigs)[number] | null>((a, r) => (a === null || r.n > a.n ? r : a), null);
57
+ const by = rig === null ? null : voidingReopen(project, events, RIG_REVIEW, Date.parse(rig.at ?? ''));
58
+ if (rig !== null && rig.accepted !== null && by !== null) {
59
+ const path = `${rig.path}.verdicts[${rig.accepted.verdict}]`;
60
+ out.push({ code: 'REOPEN_VERDICT_LAPSED', path, message: `${path}: the rig-review acceptance of rig v${rig.n} was voided by ${by}; register a new rig version with rig-create rig-version <n>, ask the person to review it in the review tool, and record the acceptance with rig-create review <n> --verdict accepted --review rig-review` });
61
+ }
62
+ }
63
+
64
+ let direction: { path: string; n: number; at: number } | null = null;
65
+ let directionLive = false;
66
+ for (const v of asArray(project.versions)) {
67
+ if (!isObject(v) || v.kind !== 'post' || typeof v.n !== 'number') continue;
68
+ const index = acceptedReviewVerdict(v, DIRECTION_REVIEW);
69
+ const verdict = asArray(v.verdicts)[index];
70
+ if (index < 0 || !isObject(verdict)) continue;
71
+ const at = Date.parse(String(verdict.at));
72
+ if (liveSignoff(events, DIRECTION_REVIEW, at)) directionLive = true;
73
+ else if (direction === null || at > direction.at) direction = { path: `versions[v${v.n}].verdicts[${index}]`, n: v.n, at };
74
+ }
75
+ const by = direction === null || directionLive ? null : voidingReopen(project, events, DIRECTION_REVIEW, direction.at);
76
+ if (direction !== null && by !== null) {
77
+ out.push({ code: 'REOPEN_VERDICT_LAPSED', path: direction.path, message: `${direction.path}: the direction-review acceptance of post v${direction.n} was voided by ${by}; ask the person to review a live post version in the review tool and record the acceptance with rig-create review <n> --verdict accepted --review direction-review` });
78
+ }
79
+ return out;
80
+ }