@rig-c/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
package/src/init.ts ADDED
@@ -0,0 +1,265 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import { existsSync, readFileSync, readdirSync, statSync } from './check-reads.ts';
4
+ import { basename, dirname, join, resolve } from 'node:path';
5
+
6
+ import { writeAgentGuide } from './agent-guide.ts';
7
+ import { PINNED_PACKAGES } from './pins.ts';
8
+ import { readVerbatim } from './record-input.ts';
9
+ import { refuse, USAGE_EXIT } from './refusal.ts';
10
+ import { CHOICE_ORDER_SPEC } from './stages.ts';
11
+ import { readTemplate, type JsonObject } from './templates.ts';
12
+ import { JOB_BINDING_FILE, JOB_OUTBOX_DIRECTORY } from './job-outbox.ts';
13
+
14
+ export const INTERVIEW_DOC_URL = 'https://github.com/rig-c/create/blob/main/docs/INTERVIEW.md';
15
+
16
+ /** Folders kept in git while empty (docs/PROJECT.md: tracked). Kept by a .gitkeep. */
17
+ export const TRACKED_EMPTY_DIRS = ['inputs/references', 'evidence'] as const;
18
+ /** Folders git ignores (docs/PROJECT.md: not tracked). Created empty. */
19
+ export const IGNORED_DIRS = ['derived', 'outputs', 'scratch'] as const;
20
+
21
+ export const GITIGNORE = ['/derived/', '/outputs/', '/scratch/', 'node_modules/', `/${JOB_BINDING_FILE}`, `/${JOB_OUTBOX_DIRECTORY}/`, ''].join('\n');
22
+
23
+ /** Used for the first commit only, and only when git has no identity configured. */
24
+ export const FALLBACK_IDENTITY = { name: 'rig-create', email: 'rig-create@localhost' } as const;
25
+
26
+ export interface InitOptions {
27
+ install: boolean;
28
+ /** The instant the record's adoption dates carry (ms since the epoch). */
29
+ now: number;
30
+ /** The painting the project is for, registered as its first input; with it init refuses a second project for the same artwork. */
31
+ painting?: PaintingOption;
32
+ }
33
+
34
+ export interface SameArtworkOptions {
35
+ /** Where sibling projects live; default the new project's parent folder. */
36
+ projectsRoot?: string | undefined;
37
+ /** A file saying why this is a new project for an artwork another project already binds. */
38
+ sameArtworkReason?: string | undefined;
39
+ /** The input index being registered; default 0. */
40
+ index?: number | undefined;
41
+ }
42
+
43
+ export interface PaintingOption extends SameArtworkOptions {
44
+ file: string;
45
+ origin: string;
46
+ }
47
+
48
+ interface Artwork { readonly path: string; readonly bytes: Buffer; readonly entry: JsonObject; readonly same: JsonObject | null }
49
+
50
+ /**
51
+ * One artwork is one project (create#285, create#291): refuse before writing anything when a
52
+ * project directly under the projects root binds an input with the painting's sha256, unless a reason is given.
53
+ */
54
+ export function sameArtworkFor(dir: string, name: string, sha256: string, now: number, options: SameArtworkOptions = {}): JsonObject | null {
55
+ const root = resolve(options.projectsRoot ?? dirname(dir));
56
+ let siblings: string[] = [];
57
+ try { siblings = readdirSync(root).sort(); } catch {
58
+ if (options.projectsRoot !== undefined) refuse('INIT_ARTWORK_USAGE', `--projects-root ${root}: not a readable folder`, USAGE_EXIT);
59
+ }
60
+ const matches = siblings.filter(sibling => {
61
+ if (resolve(root, sibling) === dir) return false;
62
+ let record: unknown;
63
+ try { record = JSON.parse(readFileSync(join(root, sibling, 'project.json'), 'utf8')); } catch { return false; }
64
+ const inputs = record !== null && typeof record === 'object' && 'inputs' in record && Array.isArray(record.inputs) ? record.inputs as unknown[] : [];
65
+ return inputs.some(input => input !== null && typeof input === 'object' && (input as JsonObject).sha256 === sha256);
66
+ });
67
+ if (matches.length > 0 && options.sameArtworkReason === undefined) {
68
+ refuse('INIT_ARTWORK_EXISTS', `inputs[${options.index ?? 0}]: ${name} (sha256 ${sha256}) is already an input of ${matches.join(', ')} under ${root}; one artwork is one project, so record a redo, a new source or a new tool as a version there, or give --same-artwork-reason <file> for a different rig kind`);
69
+ }
70
+ if (options.sameArtworkReason !== undefined) {
71
+ if (matches.length === 0) refuse('INIT_ARTWORK_USAGE', `--same-artwork-reason: no project under ${root} binds ${name}; drop the reason`, USAGE_EXIT);
72
+ const reason = readVerbatim(options.sameArtworkReason).trim();
73
+ if (!reason) refuse('INIT_ARTWORK_USAGE', '--same-artwork-reason: the reason file is empty; say why this artwork needs a second project', USAGE_EXIT);
74
+ return { reason, at: new Date(now).toISOString(), projects: matches };
75
+ }
76
+ return null;
77
+ }
78
+
79
+ function artworkFor(dir: string, painting: PaintingOption, now: number): Artwork {
80
+ let bytes: Buffer;
81
+ try {
82
+ if (!statSync(painting.file).isFile()) throw new Error('not a file');
83
+ bytes = readFileSync(painting.file);
84
+ } catch { refuse('INIT_ARTWORK_USAGE', `--painting ${painting.file}: not a readable file`, USAGE_EXIT); }
85
+ const name = basename(painting.file);
86
+ if (name.startsWith('.') || name === 'references') refuse('INIT_ARTWORK_USAGE', `--painting ${painting.file}: name the painting file so it can sit in inputs/`, USAGE_EXIT);
87
+ const sha256 = createHash('sha256').update(bytes).digest('hex');
88
+ const same = sameArtworkFor(dir, name, sha256, now, painting);
89
+ const path = `inputs/${name}`;
90
+ return { path, bytes, same, entry: { path, sha256, bytes: bytes.length, origin: painting.origin, at: new Date(now).toISOString() } };
91
+ }
92
+
93
+ export interface InitResult {
94
+ dir: string;
95
+ name: string;
96
+ installed: boolean;
97
+ /** Why `bun install` failed, when it did; the project is still created and committed without bun.lock. */
98
+ installError: string | null;
99
+ committed: string[];
100
+ commit: string;
101
+ fallbackIdentity: boolean;
102
+ }
103
+
104
+ /** A package name npm accepts, from a folder name. */
105
+ export function packageNameFor(folder: string): string {
106
+ const name = folder
107
+ .toLowerCase()
108
+ .replace(/[^a-z0-9._-]+/g, '-')
109
+ .replace(/^[._-]+/, '')
110
+ .slice(0, 214);
111
+ return name === '' ? 'rig-project' : name;
112
+ }
113
+
114
+ export function run(cmd: string[], cwd: string): { ok: boolean; out: string } {
115
+ const proc = Bun.spawnSync(cmd, { cwd, stdout: 'pipe', stderr: 'pipe' });
116
+ const out = `${proc.stdout.toString()}${proc.stderr.toString()}`.trim();
117
+ return { ok: proc.exitCode === 0, out };
118
+ }
119
+
120
+ export function checkTarget(dir: string): void {
121
+ if (!existsSync(dir)) return;
122
+ if (!statSync(dir).isDirectory()) {
123
+ refuse('INIT_NOT_A_DIRECTORY', `${dir} exists and is not a directory`);
124
+ }
125
+ const entries = readdirSync(dir).sort();
126
+ if (entries.length > 0) {
127
+ const shown = entries.slice(0, 5).join(', ') + (entries.length > 5 ? ', …' : '');
128
+ refuse('INIT_DIR_NOT_EMPTY', `${dir} is not empty (${entries.length} entries: ${shown})`);
129
+ }
130
+ }
131
+
132
+ /** The record init writes, and restart starts from: the template with its contracts adopted at `now`. */
133
+ export function freshProjectRecord(name: string, now: number): JsonObject {
134
+ const record = readTemplate('project.json');
135
+ record.name = name;
136
+ const at = new Date(now).toISOString();
137
+ record.corrections_contract = { schema: 'rig-create/corrections@1', adopted_at: at };
138
+ record.corrections = [];
139
+ const interview = record.interview as JsonObject;
140
+ interview.order = { spec: CHOICE_ORDER_SPEC, adopted_at: at, choices_adopted_at: at };
141
+ return record;
142
+ }
143
+
144
+ export function writePackageJson(dir: string, name: string): void {
145
+ const pkg = {
146
+ name: packageNameFor(name),
147
+ private: true,
148
+ type: 'module',
149
+ dependencies: { ...PINNED_PACKAGES },
150
+ };
151
+ writeFileSync(join(dir, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`);
152
+ }
153
+
154
+ export function hasGitIdentity(dir: string): boolean {
155
+ return run(['git', 'config', 'user.name'], dir).ok && run(['git', 'config', 'user.email'], dir).ok;
156
+ }
157
+
158
+ /**
159
+ * Creates a project in `target`: the layout of docs/PROJECT.md, project.json
160
+ * from the template, .gitignore, a package.json pinning the rig-c packages,
161
+ * the install (unless skipped), the agent guide, and a first commit holding
162
+ * exactly the files written here. Refuses before writing anything if the
163
+ * target exists and is not an empty directory.
164
+ */
165
+ export function initProject(target: string, options: InitOptions): InitResult {
166
+ const dir = resolve(target);
167
+ checkTarget(dir);
168
+ const name = basename(dir);
169
+ const artwork = options.painting === undefined ? null : artworkFor(dir, options.painting, options.now);
170
+
171
+ mkdirSync(dir, { recursive: true });
172
+ const written: string[] = [];
173
+
174
+ const record = freshProjectRecord(name, options.now);
175
+ if (artwork !== null) {
176
+ mkdirSync(join(dir, 'inputs'), { recursive: true });
177
+ writeFileSync(join(dir, artwork.path), artwork.bytes);
178
+ written.push(artwork.path);
179
+ record.inputs = [artwork.entry];
180
+ record.painting = artwork.path;
181
+ if (artwork.same !== null) record.same_artwork = artwork.same;
182
+ }
183
+ writeFileSync(join(dir, 'project.json'), `${JSON.stringify(record, null, 2)}\n`);
184
+ written.push('project.json');
185
+ writePackageJson(dir, name);
186
+ written.push('package.json');
187
+ writeFileSync(join(dir, '.gitignore'), GITIGNORE);
188
+ written.push('.gitignore');
189
+
190
+ for (const sub of TRACKED_EMPTY_DIRS) {
191
+ mkdirSync(join(dir, sub), { recursive: true });
192
+ writeFileSync(join(dir, sub, '.gitkeep'), '');
193
+ written.push(`${sub}/.gitkeep`);
194
+ }
195
+ for (const sub of IGNORED_DIRS) {
196
+ mkdirSync(join(dir, sub), { recursive: true });
197
+ }
198
+
199
+ written.push(...writeAgentGuide(dir));
200
+
201
+ // An install failure does not stop init: the project and its first commit
202
+ // are still made (without bun.lock), so the recovery is to run `bun install`
203
+ // in the project and commit bun.lock — never to delete it and start again.
204
+ let installError: string | null = null;
205
+ if (options.install) {
206
+ const install = run([process.execPath, 'install'], dir);
207
+ if (install.ok) written.push('bun.lock');
208
+ else installError = install.out;
209
+ }
210
+
211
+ const gitInit = run(['git', 'init', '-q'], dir);
212
+ if (!gitInit.ok) refuse('INIT_GIT_FAILED', `git init failed in ${dir}\n${gitInit.out}`);
213
+
214
+ const add = run(['git', 'add', '--', ...written], dir);
215
+ if (!add.ok) refuse('INIT_GIT_FAILED', `git add failed in ${dir}\n${add.out}`);
216
+
217
+ const fallbackIdentity = !hasGitIdentity(dir);
218
+ const identity = fallbackIdentity
219
+ ? ['-c', `user.name=${FALLBACK_IDENTITY.name}`, '-c', `user.email=${FALLBACK_IDENTITY.email}`]
220
+ : [];
221
+ const commit = run(['git', ...identity, 'commit', '-q', '-m', `Create project ${name}`], dir);
222
+ if (!commit.ok) refuse('INIT_GIT_FAILED', `git commit failed in ${dir}\n${commit.out}`);
223
+
224
+ const head = run(['git', 'rev-parse', '--short', 'HEAD'], dir);
225
+ return {
226
+ dir,
227
+ name,
228
+ installed: options.install && installError === null,
229
+ installError,
230
+ committed: written,
231
+ commit: head.out,
232
+ fallbackIdentity,
233
+ };
234
+ }
235
+
236
+ /** What init prints on success. */
237
+ export function describeInit(result: InitResult): string {
238
+ const pins = Object.entries(PINNED_PACKAGES)
239
+ .map(([pkg, version]) => `${pkg} ${version}`)
240
+ .join(', ');
241
+ const lines = [
242
+ `Created project ${result.name} at ${result.dir}`,
243
+ result.installed
244
+ ? ` installed ${pins} (bun.lock written)`
245
+ : result.installError === null
246
+ ? ` pinned ${pins}; install skipped (--no-install)`
247
+ : ` pinned ${pins}; bun install FAILED, so bun.lock is not in the first commit`,
248
+ ` first commit ${result.commit}: ${result.committed.join(', ')}`,
249
+ ];
250
+ if (result.fallbackIdentity) {
251
+ lines.push(
252
+ ` no git identity is configured; the first commit is authored as "${FALLBACK_IDENTITY.name} <${FALLBACK_IDENTITY.email}>" (that commit only, nothing written to git config)`,
253
+ );
254
+ }
255
+ lines.push('', 'Next:');
256
+ let step = 1;
257
+ if (!result.installed) {
258
+ lines.push(` ${step++}. cd ${result.dir} && bun install, then commit bun.lock`);
259
+ }
260
+ const painting = result.committed.find(path => path.startsWith('inputs/') && !path.endsWith('.gitkeep'));
261
+ lines.push(painting === undefined
262
+ ? ` ${step++}. put the painting in inputs/, then start the interview: ${INTERVIEW_DOC_URL}`
263
+ : ` ${step++}. ${painting} is registered; start the interview: ${INTERVIEW_DOC_URL}`);
264
+ return lines.join('\n');
265
+ }
package/src/inputs.ts ADDED
@@ -0,0 +1,160 @@
1
+ /** Register immutable source bytes without interpreting producer content. */
2
+ import { createHash } from 'node:crypto';
3
+ import { closeSync, fchmodSync, openSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
4
+ import { readFileSync, realpathSync, statSync } from './check-reads.ts';
5
+ import { isAbsolute, relative, resolve, sep } from 'node:path';
6
+ import { asArray, isObject, readJsonObject, type Ctx } from './check.ts';
7
+ import { sameArtworkFor } from './init.ts';
8
+ import { Refusal, refuse, USAGE_EXIT } from './refusal.ts';
9
+ import type { Json, JsonObject } from './templates.ts';
10
+
11
+ export const INPUT_WRITER_SINCE = '2026-11-08T00:00:00Z';
12
+ const text = (value: Json | undefined): value is string => typeof value === 'string' && value.trim() !== '';
13
+ const sha256 = (bytes: Uint8Array): string => createHash('sha256').update(bytes).digest('hex');
14
+ const escapes = (path: string): boolean => isAbsolute(path) || path === '..' || path.startsWith(`..${sep}`);
15
+
16
+ function inputPath(dir: string, name: string): string {
17
+ const path = relative(dir, resolve(dir, name));
18
+ if (isAbsolute(name) || escapes(path) || path === '' || path === 'project.json' || path === '.input.lock') {
19
+ refuse('INPUT_PATH_INVALID', `inputs[].path: '${name}' must name a project-relative source file other than project.json; retain the source inside the project`);
20
+ }
21
+ return path.split(sep).join('/');
22
+ }
23
+
24
+ function sourceBytes(dir: string, name: string, path: string): Buffer {
25
+ const file = resolve(dir, name);
26
+ try {
27
+ const target = realpathSync(file);
28
+ const source = statSync(file);
29
+ const record = statSync(resolve(dir, 'project.json'));
30
+ if (escapes(relative(realpathSync(dir), target)) || (source.dev === record.dev && source.ino === record.ino) || target === resolve(realpathSync(dir), '.input.lock')) {
31
+ refuse('INPUT_PATH_INVALID', `${path}: '${name}' resolves outside the project, to project.json or to the writer lock; retain a contained source file`);
32
+ }
33
+ if (!source.isFile()) throw new Error('not a regular file');
34
+ return readFileSync(file);
35
+ } catch (error) {
36
+ if (error instanceof Refusal) throw error;
37
+ refuse('INPUT_FILE_UNREADABLE', `${path}: cannot read source file '${name}'; restore the retained file before registering or checking it`);
38
+ }
39
+ }
40
+
41
+ /** The writer and check use the same shape, containment and raw-byte validator. */
42
+ export function checkInputs(ctx: Pick<Ctx, 'dir' | 'project' | 'refusals' | 'warnings'>): void {
43
+ const painting = ctx.project.painting;
44
+ if (painting !== undefined && (!text(painting) || !asArray(ctx.project.inputs).some(input => isObject(input) && input.path === painting))) {
45
+ ctx.refusals.push({ code: 'INPUT_PAINTING_INVALID', path: 'painting', message: 'painting: expected the path of one registered input; restore the declaration written by init --painting or input add --painting' });
46
+ }
47
+ if (ctx.project.inputs !== undefined && !Array.isArray(ctx.project.inputs)) {
48
+ ctx.refusals.push({ code: 'INPUTS_INVALID', path: 'inputs', message: 'inputs: expected an array; restore the input records and register new files with input add' });
49
+ return;
50
+ }
51
+ const paths = new Set<string>();
52
+ asArray(ctx.project.inputs).forEach((raw, index) => {
53
+ const path = `inputs[${index}]`;
54
+ const entry = isObject(raw) ? raw : {};
55
+ const at = typeof entry.at === 'string' ? Date.parse(entry.at) : NaN;
56
+ const complete = text(entry.path) && typeof entry.sha256 === 'string' && /^[a-f0-9]{64}$/.test(entry.sha256)
57
+ && typeof entry.bytes === 'number' && Number.isSafeInteger(entry.bytes) && entry.bytes >= 0 && text(entry.origin) && Number.isFinite(at);
58
+ if (!complete) {
59
+ const legacy = !Number.isFinite(at) || at < Date.parse(INPUT_WRITER_SINCE);
60
+ (legacy ? ctx.warnings : ctx.refusals).push({ code: 'INPUT_WRITER_MISSING', path,
61
+ message: `${path}: '${String(entry.path ?? '(missing path)')}' lacks writer metadata {path, sha256, bytes, origin, at}${legacy ? '; legacy record retained' : ''}; preserve historical bytes and register a new source name with input add --origin` });
62
+ return;
63
+ }
64
+ try {
65
+ const name = inputPath(ctx.dir, entry.path as string);
66
+ if (paths.has(name)) ctx.refusals.push({ code: 'INPUT_PATH_DUPLICATE', path: `${path}.path`, message: `${path}.path: '${name}' is already registered; keep one record per source name and use a new name for changed bytes` });
67
+ paths.add(name);
68
+ const bytes = sourceBytes(ctx.dir, name, `${path}.path`);
69
+ if (sha256(bytes) !== entry.sha256 || bytes.length !== entry.bytes) ctx.refusals.push({ code: 'INPUT_FILE_CHANGED', path, message: `${path}: '${name}' differs from its recorded raw sha256 or bytes; restore the original file and register changed bytes under a new name` });
70
+ } catch (error) {
71
+ if (!(error instanceof Refusal)) throw error;
72
+ ctx.refusals.push({ code: error.code, path: `${path}.path`, message: `${path}.path: ${error.message}` });
73
+ }
74
+ });
75
+ }
76
+
77
+ export const INPUT_HELP = `input add <path> [<project dir>] --origin <words> [--painting [--projects-root <dir>] [--same-artwork-reason <file>]] [--json]
78
+ Register raw source bytes as {path, sha256, bytes, origin, at}.
79
+ --painting declares the project's one painting and checks sibling inputs for the same artwork.
80
+ Paths are project-relative. Changed inputs require a new file name.`;
81
+
82
+ export function inputCommand(args: string[], now: number): { stdout: string; exit: number } {
83
+ if (args[0] !== 'add') refuse('INPUT_USAGE', INPUT_HELP, USAGE_EXIT);
84
+ const positional: string[] = [];
85
+ let origin: string | undefined;
86
+ let projectsRoot: string | undefined;
87
+ let sameArtworkReason: string | undefined;
88
+ let painting = false;
89
+ let json = false;
90
+ for (let index = 1; index < args.length; index++) {
91
+ const arg = args[index]!;
92
+ if (arg === '--json' && !json) json = true;
93
+ else if (arg === '--painting' && !painting) painting = true;
94
+ else if (arg === '--origin' && origin === undefined) {
95
+ origin = args[++index];
96
+ if (!origin || origin.startsWith('--') || !origin.trim()) refuse('INPUT_USAGE', 'inputs[].origin: supply --origin with non-empty source words', USAGE_EXIT);
97
+ } else if (arg === '--projects-root' && projectsRoot === undefined) {
98
+ projectsRoot = args[++index];
99
+ if (!projectsRoot || projectsRoot.startsWith('--') || !projectsRoot.trim()) refuse('INPUT_USAGE', 'inputs: supply --projects-root with a directory path', USAGE_EXIT);
100
+ } else if (arg === '--same-artwork-reason' && sameArtworkReason === undefined) {
101
+ sameArtworkReason = args[++index];
102
+ if (!sameArtworkReason || sameArtworkReason.startsWith('--') || !sameArtworkReason.trim()) refuse('INPUT_USAGE', 'inputs: supply --same-artwork-reason with a file path', USAGE_EXIT);
103
+ } else if (arg.startsWith('-')) refuse('INPUT_USAGE', `inputs: unexpected or repeated option '${arg}'; ${INPUT_HELP}`, USAGE_EXIT);
104
+ else positional.push(arg);
105
+ }
106
+ if (positional.length < 1 || positional.length > 2 || origin === undefined) refuse('INPUT_USAGE', INPUT_HELP, USAGE_EXIT);
107
+ if (!painting && (projectsRoot !== undefined || sameArtworkReason !== undefined)) refuse('INPUT_USAGE', 'painting: --projects-root and --same-artwork-reason require input add --painting', USAGE_EXIT);
108
+ const dir = resolve(positional[1] ?? process.cwd());
109
+ const name = inputPath(dir, positional[0]!);
110
+ const file = resolve(dir, 'project.json');
111
+ const lock = resolve(dir, '.input.lock');
112
+ let fd: number;
113
+ try { fd = openSync(lock, 'wx'); } catch { refuse('INPUT_SOURCE_CHANGED', 'inputs: another input writer holds the project lock; wait for it to finish and retry against frozen source bytes'); }
114
+ let published = false;
115
+ try {
116
+ let before: Buffer;
117
+ try { before = readFileSync(file); } catch { refuse('PROJECT_JSON_INVALID', 'project.json: restore a readable project record before input add'); }
118
+ const read = readJsonObject(file);
119
+ if (!read.ok) refuse('PROJECT_JSON_INVALID', `project.json: ${read.error}`);
120
+ const project = read.value;
121
+ if (project.schema !== 'rig-create/project@0') refuse('SCHEMA_UNKNOWN', 'project.json.schema: input add reads rig-create/project@0');
122
+ if (painting && project.painting !== undefined) refuse('INPUT_PAINTING_EXISTS', `painting: '${String(project.painting)}' is already declared; retain the project's painting and register other sources without --painting`);
123
+ const bytes = sourceBytes(dir, name, 'inputs[].path');
124
+ const entry: JsonObject = { path: name, sha256: sha256(bytes), bytes: bytes.length, origin, at: new Date(now).toISOString() };
125
+ const existing = asArray(project.inputs).filter(isObject).find(input => text(input.path) && resolve(dir, input.path) === resolve(dir, name));
126
+ if (existing) {
127
+ if (existing.sha256 !== entry.sha256 || existing.bytes !== entry.bytes) refuse('INPUT_FILE_CHANGED', `inputs[].path: '${name}' has changed under its registered name; restore the old bytes and register a new file name`);
128
+ refuse('INPUT_PATH_DUPLICATE', `inputs[].path: '${name}' is already registered; keep its record and use a new name for changed bytes`);
129
+ }
130
+ const ctx: Pick<Ctx, 'dir' | 'project' | 'refusals' | 'warnings'> = { dir, project, refusals: [], warnings: [] };
131
+ if (project.inputs !== undefined && !Array.isArray(project.inputs)) {
132
+ checkInputs(ctx);
133
+ refuse(ctx.refusals[0]!.code, ctx.refusals[0]!.message);
134
+ }
135
+ if (painting) {
136
+ const same = sameArtworkFor(dir, name, String(entry.sha256), now, {
137
+ projectsRoot,
138
+ sameArtworkReason,
139
+ index: asArray(project.inputs).length,
140
+ });
141
+ project.painting = name;
142
+ if (same !== null && project.same_artwork === undefined) project.same_artwork = same;
143
+ }
144
+ project.inputs = [...asArray(project.inputs), entry];
145
+ checkInputs(ctx);
146
+ const finding = ctx.refusals[0];
147
+ if (finding) refuse(finding.code, finding.message);
148
+ try {
149
+ fchmodSync(fd, statSync(file).mode);
150
+ writeFileSync(fd, `${JSON.stringify(project, null, 2)}\n`);
151
+ if (!readFileSync(file).equals(before) || !sourceBytes(dir, name, 'inputs[].path').equals(bytes)) refuse('INPUT_SOURCE_CHANGED', `inputs[].path: '${name}' or project.json changed during registration; freeze both files and retry`);
152
+ renameSync(lock, file);
153
+ published = true;
154
+ } catch (error) {
155
+ if (error instanceof Refusal) throw error;
156
+ refuse('INPUT_WRITE_FAILED', 'inputs: could not publish the staged project.json record; restore writable project storage and retry');
157
+ }
158
+ return { stdout: json ? JSON.stringify(entry, null, 2) : `input: recorded '${name}' (${bytes.length} bytes)`, exit: 0 };
159
+ } finally { closeSync(fd); if (!published) unlinkSync(lock); }
160
+ }
package/src/intake.ts ADDED
@@ -0,0 +1,158 @@
1
+ /** Advisory intake: consume immutable producer bytes, never pixels or producer verifiers. */
2
+ import { renameSync, writeFileSync } from 'node:fs';
3
+ import { readFileSync } from './check-reads.ts';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { isDeepStrictEqual as equal } from 'node:util';
6
+ import { asArray, classify, isObject, type Finding, type Item } from './check.ts';
7
+ import { choiceAdoption, literalFile, optionFile, selection, sha256, validateChoice, validateContext, validateSet } from './choices.ts';
8
+ import { structurePathIsIgnored } from './structure.ts';
9
+ import { readProject } from './feedback.ts';
10
+ import { refuse } from './refusal.ts';
11
+ import type { Json, JsonObject } from './templates.ts';
12
+
13
+ export const INTAKE_SPEC = 'rig-create/intake@1';
14
+ export const PREFLIGHT_SPEC = 'rig-parts/head-crop-preflight/1';
15
+ export const INTAKE_QUESTION = 'intake_suitability';
16
+ export const INTAKE_NOTES = 'Colour-adjacent ground and ground enclosed between strands: known-not-predicted (two rounds of a model sweep on a pilot did not separate them). No threshold. Silhouette stepping, source-vs-final alpha classes, patchwork runs and versioned dark retention need post-assemble outputs and are excluded.';
17
+ const text = (v: Json | undefined): v is string => typeof v === 'string' && v.trim() !== '';
18
+ const clock = (v: Json | undefined): number => typeof v === 'string' ? Date.parse(v) : NaN;
19
+ const count = (v: Json | undefined): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
20
+ function invalid(why: string): never { throw Error(why); }
21
+
22
+ /** Optional item for the explicit choice writers only, never global approval readers. */
23
+ export function intakeItem(project: JsonObject): Item | undefined {
24
+ const intake = isObject(project.intake) ? project.intake : null;
25
+ const record = intake && isObject(intake.choice) ? intake.choice : null;
26
+ if (!record) return undefined;
27
+ return { id: INTAKE_QUESTION, kind: 'framing', path: 'intake.choice.answer', record, raw: record.answer ?? null, state: classify(record.answer, { collection: null }) };
28
+ }
29
+
30
+ export function intakeContext(project: JsonObject, at: number): JsonObject {
31
+ const intake = isObject(project.intake) ? project.intake : {};
32
+ const reports = asArray(intake.reports).filter(isObject).filter(r => clock(r.at) <= at);
33
+ const entry = reports.at(-1);
34
+ if (intake.spec !== INTAKE_SPEC || !entry || !isObject(entry.report)) invalid('intake.reports: record a preflight before showing the parts-stage ask');
35
+ return { kind: 'intake', report_id: entry.id as Json, report: entry.report };
36
+ }
37
+
38
+ /** Validate the producer's report shape; do not rederive registration or count measurements. */
39
+ export function readIntakeReport(dir: string, binding: JsonObject): JsonObject {
40
+ if (!text(binding.path) || typeof binding.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(binding.sha256)) invalid('report needs a retained path and raw sha256');
41
+ const file = literalFile(dir, binding.path);
42
+ if (structurePathIsIgnored(dir, binding.path)) invalid('report must survive a clone; retain it outside ignored roots');
43
+ const bytes = readFileSync(file);
44
+ if (sha256(bytes) !== binding.sha256) invalid('report sha256 differs from retained bytes');
45
+ const raw: Json = JSON.parse(bytes.toString('utf8'));
46
+ if (!isObject(raw) || raw.spec !== PREFLIGHT_SPEC || raw.drawn_alpha_above !== 8 || raw.pixel_grid !== 'full-run') invalid('expected rig-parts/head-crop-preflight/1, native alpha > 8 on the full-run grid');
47
+ if (!Array.isArray(raw.head_box) || raw.head_box.length !== 4 || !raw.head_box.every(n => typeof n === 'number' && Number.isFinite(n)) || Number(raw.head_box[0]) >= Number(raw.head_box[2]) || Number(raw.head_box[1]) >= Number(raw.head_box[3])) invalid('invalid head_box');
48
+ for (const key of ['source_canvas', 'run_canvas']) {
49
+ const canvas = raw[key];
50
+ if (!isObject(canvas) || !count(canvas.w) || !count(canvas.h) || !canvas.w || !canvas.h) invalid(`invalid ${key}`);
51
+ }
52
+ if (!isObject(raw.registration) || typeof raw.registration.scale_source_per_run !== 'number' || !Number.isFinite(raw.registration.scale_source_per_run) || raw.registration.scale_source_per_run <= 0 || !['pad_left','pad_top'].every(k => typeof (raw.registration as JsonObject)[k] === 'number' && Number.isFinite((raw.registration as JsonObject)[k]))) invalid('invalid registration');
53
+ const tags = new Set<string>();
54
+ if (!Array.isArray(raw.layers)) invalid('layers must be an array');
55
+ for (const layer of raw.layers) {
56
+ if (!isObject(layer) || !text(layer.tag) || tags.has(layer.tag) || !count(layer.drawn_px) || !count(layer.bottom_px) || !isObject(layer.sides) || !['top_px','left_px','right_px'].every(k => count((layer.sides as JsonObject)[k]))) invalid('invalid or duplicate layer counts');
57
+ tags.add(layer.tag);
58
+ if ([layer.bottom_px, ...Object.values(layer.sides)].some(n => typeof n !== 'number' || n > Number(layer.drawn_px))) invalid('edge count exceeds drawn samples');
59
+ }
60
+ for (const key of ['spec','head_box','source_canvas','run_canvas']) if (key in binding && !equal(binding[key], raw[key])) invalid(`report binding ${key} differs from producer bytes`);
61
+ return raw;
62
+ }
63
+ export function intakeHit(report: JsonObject): boolean {
64
+ return asArray(report.layers).filter(isObject).some(l => isObject(l.sides) && ['top_px','left_px','right_px'].some(k => Number((l.sides as JsonObject)[k]) > 0));
65
+ }
66
+ function warning(code: string, path: string, why: string, remedy: string): Finding { return { code, path, message: `${path}: ${why}; ${remedy}` }; }
67
+
68
+ export function intakeWarnings(dir: string, project: JsonObject): Finding[] {
69
+ if (project.intake === undefined) return [];
70
+ const warnings: Finding[] = [];
71
+ const addInvalid = (path: string, e: unknown) => warnings.push(warning('INTAKE_REPORT_INVALID',path,String(e),'restore the original producer report bytes first, then append a corrected entry with intake-report'));
72
+ const intake = project.intake;
73
+ if (!isObject(intake) || intake.spec !== INTAKE_SPEC || !Array.isArray(intake.reports) || !intake.reports.length) {
74
+ addInvalid('intake','expected rig-create/intake@1 with nonempty reports'); return warnings;
75
+ }
76
+ const ids = new Set<string>();
77
+ let active: JsonObject | undefined;
78
+ for (const [i, entry] of intake.reports.entries()) {
79
+ const path = `intake.reports[${i}]`;
80
+ try {
81
+ if (!isObject(entry) || !text(entry.id) || ids.has(entry.id) || !text(entry.by) || !Number.isFinite(clock(entry.at)) || !text(entry.parts_version) || !isObject(entry.report)) invalid('invalid report identity, clock, declared parts version or binding');
82
+ if (entry.supersedes !== undefined && (!text(entry.supersedes) || !ids.has(entry.supersedes))) invalid('supersedes must name an earlier retained entry');
83
+ ids.add(entry.id);
84
+ if (!['spec','head_box','source_canvas','run_canvas'].every(k => k in (entry.report as JsonObject))) invalid('report binding must retain spec, head_box and both canvases');
85
+ const raw = readIntakeReport(dir, entry.report);
86
+ if (!equal(entry.measurement,raw) || entry.prediction !== (intakeHit(raw) ? 'hit' : 'no-hit')) invalid('copied report fields or prediction differ from retained report');
87
+ if (i === intake.reports.length-1) active = entry;
88
+ } catch (e) { addInvalid(path,e); }
89
+ }
90
+ if (active?.prediction === 'hit') {
91
+ const raw = active.measurement as JsonObject;
92
+ const counts = asArray(raw.layers).filter(isObject).map(l => `${String(l.tag)} ${JSON.stringify(l.sides)} (bottom ${String(l.bottom_px)})`).join('; ');
93
+ warnings.push(warning('INTAKE_HEAD_CROP_RISK','intake.reports',`advisory native alpha > 8 before ghost cleanup: ${counts}`,'show the report-bound parts-stage intake_suitability choice; no approval is refused'));
94
+ }
95
+ const item = intakeItem(project);
96
+ let pending = true;
97
+ if (item) {
98
+ try {
99
+ if (item.record.pending_options !== undefined) {
100
+ const {set} = optionFile(dir,item.record.pending_options); validateSet(project,item,set); validateContext(dir,project,set,clock(set.drafted_at));
101
+ }
102
+ if (item.raw !== null) {
103
+ if (!isObject(item.raw) || item.raw.basis === 'inherited') invalid('intake choice must be a local per-ask record');
104
+ validateChoice(dir,project,item,item.raw);
105
+ const {set} = optionFile(dir,(item.raw.choice as JsonObject).set);
106
+ const chosen = selection(set,item.raw.choice as JsonObject);
107
+ const context = set.context as JsonObject;
108
+ pending = !active || context.report_id !== active.id || chosen.pending.length > 0 || (chosen.autos.length > 0 && !(isObject(item.raw.agent_decision) && 'value' in item.raw.agent_decision));
109
+ }
110
+ if (!Array.isArray(item.record.history)) invalid('intake choice history must be an array');
111
+ for (const answer of item.record.history) {
112
+ if (!isObject(answer)) invalid('invalid choice history');
113
+ validateChoice(dir,project,item,answer);
114
+ }
115
+ } catch (e) { pending = true; warnings.push(warning('INTAKE_CHOICE_INVALID','intake.choice',String(e),'retain and re-show report-bound options with the interview choice writers')); }
116
+ } else if (intake.choice !== undefined) warnings.push(warning('INTAKE_CHOICE_INVALID','intake.choice','invalid optional choice record','record a new report before showing options'));
117
+ if (project.stage === 'parts' && active?.prediction === 'hit' && pending) warnings.push(warning('INTAKE_CHOICE_PENDING','intake.choice','HIT report has no resolved choice bound to it',`${choiceAdoption(project) === null ? 'adopt choices with interview-options --adopt first, then ' : ''}show intake_suitability in parts and record the person’s explicit pick, Other or delegation; advisory only`));
118
+ return warnings;
119
+ }
120
+
121
+ export const INTAKE_HELP = `intake-report [<dir>] --id <id> --report <retained relative path> --sha256 <raw sha> --parts-version <declared version> --by <agent> [--supersedes <id>]
122
+ Record a parts head-crop preflight without running a producer. Offers optional intake_suitability in parts.
123
+ intake [<dir>]
124
+ Show advisory counts and choice state; never refuse approval. This cannot save the first full run.`;
125
+ export function intakeReportCommand(args: string[], now: number): string {
126
+ const opts: Record<string,string> = {}; const positional: string[] = [];
127
+ for (let i=0;i<args.length;i++) {
128
+ const arg = args[i] as string;
129
+ if (!arg.startsWith('-')) { positional.push(arg); continue; }
130
+ if (!['--id','--report','--sha256','--parts-version','--by','--supersedes'].includes(arg) || opts[arg] !== undefined || !args[i+1] || args[i+1]?.startsWith('--')) refuse('INTAKE_REPORT_INVALID',`intake.reports: invalid writer arguments; ${INTAKE_HELP}`);
131
+ opts[arg] = args[++i] as string;
132
+ }
133
+ if (positional.length > 1 || !['--id','--report','--sha256','--parts-version','--by'].every(k => opts[k]?.trim())) refuse('INTAKE_REPORT_INVALID',`intake.reports: missing writer arguments; ${INTAKE_HELP}`);
134
+ const dir = resolve(positional[0] ?? process.cwd()); const {file,project} = readProject(dir);
135
+ const next = structuredClone(project);
136
+ try {
137
+ if (project.stage !== 'parts') invalid('offer intake only in the parts stage at report time');
138
+ if (next.intake !== undefined && intakeWarnings(dir,next).some(w => w.code === 'INTAKE_REPORT_INVALID')) invalid('restore existing retained intake reports before appending');
139
+ if (next.intake === undefined) next.intake = {spec:INTAKE_SPEC,reports:[],choice:{id:INTAKE_QUESTION,answer:null,history:[]}};
140
+ const intake = next.intake as JsonObject; const reports = intake.reports as Json[];
141
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(opts['--id'] as string) || reports.some(r => isObject(r) && r.id === opts['--id'])) invalid('use a new report id');
142
+ const binding: JsonObject = {path:opts['--report'] as string,sha256:opts['--sha256'] as string};
143
+ const measurement = readIntakeReport(dir,binding);
144
+ for (const k of ['spec','head_box','source_canvas','run_canvas']) binding[k] = measurement[k] as Json;
145
+ reports.push({id:opts['--id'] as string,report:binding,parts_version:opts['--parts-version'] as string,measurement,prediction:intakeHit(measurement)?'hit':'no-hit',by:opts['--by'] as string,at:new Date(now).toISOString(),...(opts['--supersedes']?{supersedes:opts['--supersedes']}:{})});
146
+ const errors = intakeWarnings(dir,next).filter(w => w.code === 'INTAKE_REPORT_INVALID');
147
+ if (errors.length) invalid(errors[0]?.message ?? 'invalid report');
148
+ const tmp = join(dirname(file),`.intake-${crypto.randomUUID()}.tmp`);
149
+ writeFileSync(tmp,`${JSON.stringify(next,null,2)}\n`,{flag:'wx'}); renameSync(tmp,file);
150
+ return intakeCommand([dir]);
151
+ } catch (e) { refuse('INTAKE_REPORT_INVALID',`intake.reports: ${String(e)}; retain correct report bytes and retry intake-report`); }
152
+ }
153
+ export function intakeCommand(args: string[]): string {
154
+ if (args.length > 1 || args.some(a => a.startsWith('-'))) refuse('INTAKE_REPORT_INVALID',`intake: expected a project folder; ${INTAKE_HELP}`);
155
+ const dir = resolve(args[0] ?? process.cwd()); const {project} = readProject(dir);
156
+ const intake = isObject(project.intake) ? project.intake : null;
157
+ return JSON.stringify({advisory:true,notes:INTAKE_NOTES,intake:intake ?? 'not-measured',warnings:intakeWarnings(dir,project)},null,2);
158
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `rig-create interview --check [<project dir>] [--json]`: the command around
3
+ * src/check.ts. Prints every finding, exits 0 when clean, 1 when anything is
4
+ * refused, 2 on a usage error. `--json` prints the report as one JSON object
5
+ * for agents and later checks (#6, #8, #9) to read.
6
+ */
7
+ import { checkProject, formatReport } from './check.ts';
8
+ import { checkCollection, collectionDirOf, formatCollectionReport } from './pins-check.ts';
9
+ import { refuse, USAGE_EXIT } from './refusal.ts';
10
+
11
+ export interface CommandResult {
12
+ stdout: string;
13
+ exit: number;
14
+ }
15
+
16
+ export const INTERVIEW_HELP = `interview:
17
+ --check check project.json: each current interview has its guides, later rigging guides wait for approved parts,
18
+ every plan step rests on an answer,
19
+ every stage moved has its sign-off, every @rig-c/parts section has a place,
20
+ every config section of a version cites its guide, every bounce is answered by a later version (docs/CHECKS.md)
21
+ <project dir> the project folder (default: the current directory); a collection folder (or its collection.json)
22
+ runs the collection check (see collection --check)
23
+ --json print the report as JSON (ok, stage, parts, derived, provenance[], feedback[], refusals[], warnings[])`;
24
+
25
+ export function interviewCommand(args: string[], now: number): CommandResult {
26
+ let check = false;
27
+ let json = false;
28
+ const positional: string[] = [];
29
+ for (const arg of args) {
30
+ if (arg === '--check') check = true;
31
+ else if (arg === '--json') json = true;
32
+ else if (arg.startsWith('-')) refuse('UNKNOWN_OPTION', `interview does not take ${arg}`, USAGE_EXIT);
33
+ else positional.push(arg);
34
+ }
35
+ if (!check) refuse('INTERVIEW_MODE_MISSING', 'interview needs --check: rig-create interview --check [<project dir>]', USAGE_EXIT);
36
+ if (positional.length > 1) {
37
+ refuse('INTERVIEW_TOO_MANY_ARGUMENTS', `interview --check takes one folder, got ${positional.join(' ')}`, USAGE_EXIT);
38
+ }
39
+ const target = positional[0] ?? process.cwd();
40
+ // A collection is checked by the collection check (src/pins-check.ts), not refused as SCHEMA_UNKNOWN.
41
+ const collectionDir = collectionDirOf(target);
42
+ if (collectionDir !== null) {
43
+ const report = checkCollection(collectionDir);
44
+ return { stdout: json ? JSON.stringify(report, null, 2) : formatCollectionReport(report), exit: report.ok ? 0 : 1 };
45
+ }
46
+ const report = checkProject(target, undefined, { now });
47
+ return {
48
+ stdout: json ? JSON.stringify(report, null, 2) : [formatReport(report),
49
+ ...(report.invariants === null ? [] : [`invariants: ${report.invariants.entries.length} entries, ${report.invariants.gaps.length} gaps (translated from the guide; not a rig check)`]),
50
+ ].join('\n'),
51
+ exit: report.ok ? 0 : 1,
52
+ };
53
+ }