rig-create 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +163 -2
- package/docs/CHECKS.md +1665 -0
- package/docs/COLLECTIONS.md +337 -0
- package/docs/CRAFT.md +277 -0
- package/docs/INTERVIEW.md +886 -0
- package/docs/LESSONS.md +172 -0
- package/docs/PROJECT.md +2657 -0
- package/docs/SOURCE.md +225 -0
- package/package.json +44 -4
- package/src/agent-bounce.ts +27 -0
- package/src/agent-guide.ts +237 -0
- package/src/amend.ts +193 -0
- package/src/applied-review.ts +104 -0
- package/src/ask-coverage.ts +69 -0
- package/src/assemble-answers.ts +53 -0
- package/src/cg-gates.ts +211 -0
- package/src/check-reads.ts +106 -0
- package/src/check.ts +1059 -0
- package/src/checkpoint-evidence-writer.ts +125 -0
- package/src/checkpoint-evidence.ts +152 -0
- package/src/choice-writers.ts +271 -0
- package/src/choices.ts +378 -0
- package/src/cli.ts +371 -0
- package/src/collection-answer.ts +136 -0
- package/src/collection-binding-reader.ts +496 -0
- package/src/collection-binding.ts +478 -0
- package/src/collection.ts +348 -0
- package/src/config-inputs.ts +155 -0
- package/src/config-lost.ts +135 -0
- package/src/correction-writers.ts +207 -0
- package/src/corrections.ts +685 -0
- package/src/diff-classes.ts +219 -0
- package/src/diff-map.ts +248 -0
- package/src/direction-citation.ts +13 -0
- package/src/direction-writers.ts +119 -0
- package/src/direction.ts +266 -0
- package/src/drafts.ts +363 -0
- package/src/feedback.ts +744 -0
- package/src/figure-mask.ts +143 -0
- package/src/fix-ledger.ts +355 -0
- package/src/init.ts +265 -0
- package/src/inputs.ts +160 -0
- package/src/intake.ts +158 -0
- package/src/interview.ts +53 -0
- package/src/invariants.ts +132 -0
- package/src/job-acts.ts +80 -0
- package/src/job-advance.ts +65 -0
- package/src/job-close.ts +226 -0
- package/src/job-host.ts +529 -0
- package/src/job-outbox.ts +383 -0
- package/src/job-publication.ts +701 -0
- package/src/job-runtime.ts +99 -0
- package/src/job-writer.ts +123 -0
- package/src/meshes-bust.ts +63 -0
- package/src/next.ts +1506 -0
- package/src/overrides.ts +42 -0
- package/src/package-upgrades.ts +32 -0
- package/src/packages-upgrade-check.ts +6 -0
- package/src/packages-upgrade.ts +168 -0
- package/src/parts-act.ts +96 -0
- package/src/parts-check.ts +242 -0
- package/src/parts-review.ts +196 -0
- package/src/parts-sections.ts +98 -0
- package/src/parts-verdict-record.ts +265 -0
- package/src/parts-verdict.ts +33 -0
- package/src/pause.ts +118 -0
- package/src/pins-check.ts +619 -0
- package/src/pins.ts +17 -0
- package/src/plan-bar-amendments.ts +54 -0
- package/src/plan-history.ts +147 -0
- package/src/plan-writers.ts +202 -0
- package/src/plate-receipt.ts +110 -0
- package/src/pre-interview.ts +302 -0
- package/src/prepared-inputs.ts +113 -0
- package/src/progress-writers.ts +181 -0
- package/src/provenance.ts +301 -0
- package/src/record-direction-target.ts +26 -0
- package/src/record-input.ts +18 -0
- package/src/refusal-baseline.ts +55 -0
- package/src/refusal.ts +30 -0
- package/src/reopen-lapses.ts +80 -0
- package/src/reopen.ts +144 -0
- package/src/republish.ts +93 -0
- package/src/restart.ts +141 -0
- package/src/review-act-runtime.ts +16 -0
- package/src/review-act.ts +592 -0
- package/src/review-package.ts +524 -0
- package/src/review-read.ts +238 -0
- package/src/review.ts +432 -0
- package/src/rig-version-date.ts +22 -0
- package/src/rig-version.ts +1047 -0
- package/src/run-record.ts +232 -0
- package/src/scene-authority.ts +410 -0
- package/src/scene-writers.ts +144 -0
- package/src/scene.ts +284 -0
- package/src/setup-painting.ts +46 -0
- package/src/signable.ts +65 -0
- package/src/signoff-answers.ts +76 -0
- package/src/signoff-checkpoint-act.ts +95 -0
- package/src/signoff-target.ts +127 -0
- package/src/signoff.ts +331 -0
- package/src/split-captions.ts +154 -0
- package/src/stage-progress.ts +379 -0
- package/src/stages.ts +148 -0
- package/src/step-hash.ts +19 -0
- package/src/structure-snapshot.ts +109 -0
- package/src/structure.ts +166 -0
- package/src/templates.ts +16 -0
- package/src/waypoints.ts +163 -0
- package/templates/agent-guide.md +108 -0
- package/templates/collection-agent-guide.md +72 -0
- package/templates/collection.json +252 -0
- package/templates/example-chain/AGENTS.md +134 -0
- package/templates/example-chain/CLAUDE.md +3 -0
- package/templates/example-chain/README.md +13 -0
- package/templates/example-chain/chain.json +2869 -0
- package/templates/example-chain/checkpoints/0a-interview-options/AGENTS.md +134 -0
- package/templates/example-chain/checkpoints/0a-interview-options/CLAUDE.md +3 -0
- package/templates/example-chain/checkpoints/0a-interview-options/evidence/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/0a-interview-options/evidence/approval.txt +1 -0
- package/templates/example-chain/checkpoints/0a-interview-options/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/checkpoints/0a-interview-options/evidence/split-config.json +1 -0
- package/templates/example-chain/checkpoints/0a-interview-options/inputs/painting.png +0 -0
- package/templates/example-chain/checkpoints/0a-interview-options/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/0a-interview-options/package.json +9 -0
- package/templates/example-chain/checkpoints/0a-interview-options/project.json +295 -0
- package/templates/example-chain/checkpoints/0a-interview-options.json +127 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/AGENTS.md +134 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/CLAUDE.md +3 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/approval.txt +1 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/assemble.json +309 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/reference.json +59 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/interview/seethrough.json +186 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/evidence/split-config.json +1 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/inputs/painting.png +0 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/package.json +9 -0
- package/templates/example-chain/checkpoints/0b-plan-pending/project.json +685 -0
- package/templates/example-chain/checkpoints/0b-plan-pending.json +81 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/AGENTS.md +134 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/CLAUDE.md +3 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/approval.txt +1 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/assemble.json +309 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/reference.json +59 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/interview/seethrough.json +186 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/parts-check.json +127 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/evidence/split-config.json +1 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/inputs/painting.png +0 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/package.json +9 -0
- package/templates/example-chain/checkpoints/1-parts-verdict/project.json +955 -0
- package/templates/example-chain/checkpoints/1-parts-verdict.json +204 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/AGENTS.md +134 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/CLAUDE.md +3 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/approval.txt +1 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/diff-components.json +34 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/assemble.json +309 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/bones.json +190 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/reference.json +59 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/interview/seethrough.json +186 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/parts-check.json +127 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/parts-review.json +88 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/split-config.json +1 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/evidence/structure.json +30 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/inputs/painting.png +0 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/package.json +9 -0
- package/templates/example-chain/checkpoints/2-rigging-answers/project.json +1198 -0
- package/templates/example-chain/checkpoints/2-rigging-answers.json +254 -0
- package/templates/example-chain/checkpoints/3-rig-review/AGENTS.md +134 -0
- package/templates/example-chain/checkpoints/3-rig-review/CLAUDE.md +3 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/approval.txt +1 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/diff-components.json +34 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/assemble.json +309 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/bones.json +190 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/constraints.json +272 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/meshes.json +190 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/motion.json +272 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/reference.json +59 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/interview/seethrough.json +186 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/parts-check.json +127 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/parts-review.json +88 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/setup.png +0 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/split-config.json +1 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/structure.json +30 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/v1/config.json +88 -0
- package/templates/example-chain/checkpoints/3-rig-review/evidence/v1/interview-requirements.json +8 -0
- package/templates/example-chain/checkpoints/3-rig-review/inputs/painting.png +0 -0
- package/templates/example-chain/checkpoints/3-rig-review/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/3-rig-review/package.json +9 -0
- package/templates/example-chain/checkpoints/3-rig-review/project.json +1909 -0
- package/templates/example-chain/checkpoints/3-rig-review.json +424 -0
- package/templates/example-chain/checkpoints/4-direction-post/AGENTS.md +134 -0
- package/templates/example-chain/checkpoints/4-direction-post/CLAUDE.md +3 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/approval.txt +1 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/diff-components.json +34 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/assemble.json +309 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/bones.json +190 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/camera_depth.json +102 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/constraints.json +272 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/meshes.json +190 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/motion.json +272 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/post_composition.json +122 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/post_timing.json +102 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/reference.json +59 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/interview/seethrough.json +186 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/parts-check.json +127 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/parts-review.json +88 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/setup.png +0 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/split-config.json +1 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/structure.json +30 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/v1/config.json +88 -0
- package/templates/example-chain/checkpoints/4-direction-post/evidence/v1/interview-requirements.json +8 -0
- package/templates/example-chain/checkpoints/4-direction-post/inputs/painting.png +0 -0
- package/templates/example-chain/checkpoints/4-direction-post/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/checkpoints/4-direction-post/package.json +9 -0
- package/templates/example-chain/checkpoints/4-direction-post/project.json +2302 -0
- package/templates/example-chain/checkpoints/4-direction-post.json +456 -0
- package/templates/example-chain/evidence/.gitkeep +0 -0
- package/templates/example-chain/evidence/approval.txt +1 -0
- package/templates/example-chain/evidence/captions.json +101 -0
- package/templates/example-chain/evidence/diff-components.json +34 -0
- package/templates/example-chain/evidence/direction/v2.json +74 -0
- package/templates/example-chain/evidence/direction-input.json +74 -0
- package/templates/example-chain/evidence/interview/assemble.json +309 -0
- package/templates/example-chain/evidence/interview/bones.json +190 -0
- package/templates/example-chain/evidence/interview/camera_depth.json +102 -0
- package/templates/example-chain/evidence/interview/constraints.json +272 -0
- package/templates/example-chain/evidence/interview/meshes.json +190 -0
- package/templates/example-chain/evidence/interview/motion.json +272 -0
- package/templates/example-chain/evidence/interview/post_composition.json +122 -0
- package/templates/example-chain/evidence/interview/post_timing.json +102 -0
- package/templates/example-chain/evidence/interview/reference.json +59 -0
- package/templates/example-chain/evidence/interview/rig_type.json +59 -0
- package/templates/example-chain/evidence/interview/seethrough.json +186 -0
- package/templates/example-chain/evidence/parts-check.json +127 -0
- package/templates/example-chain/evidence/parts-review.json +88 -0
- package/templates/example-chain/evidence/review-reads/a12dea02d32508acc99386f894af76a14f1c85f45f804db2aa6b4dea5297973f.json +59 -0
- package/templates/example-chain/evidence/scene/s1.json +65 -0
- package/templates/example-chain/evidence/scene-citations.json +9 -0
- package/templates/example-chain/evidence/setup.png +0 -0
- package/templates/example-chain/evidence/split-config.json +1 -0
- package/templates/example-chain/evidence/structure.json +30 -0
- package/templates/example-chain/evidence/v1/config.json +88 -0
- package/templates/example-chain/evidence/v1/interview-requirements.json +8 -0
- package/templates/example-chain/inputs/background.png +0 -0
- package/templates/example-chain/inputs/painting.png +0 -0
- package/templates/example-chain/inputs/references/.gitkeep +0 -0
- package/templates/example-chain/package.json +9 -0
- package/templates/example-chain/project.json +2593 -0
- package/templates/example-diff/diff-components.json +71 -0
- package/templates/example-diff/recomposite_rig.png +0 -0
- package/templates/example-direction/approval.txt +1 -0
- package/templates/example-direction/derived/review/v1/out/check/build/rectangle.png +0 -0
- package/templates/example-direction/derived/review/v1/out/check/build/run.json +40 -0
- package/templates/example-direction/derived/review/v1/out/check/build/skeleton.atlas +8 -0
- package/templates/example-direction/derived/review/v1/out/check/build/skeleton.json +73 -0
- package/templates/example-direction/derived/review/v1/out/check/build/skeleton.model.json +195 -0
- package/templates/example-direction/evidence/direction/v2.json +69 -0
- package/templates/example-direction/evidence/direction/v3.json +59 -0
- package/templates/example-direction/evidence/images/rectangle.png +0 -0
- package/templates/example-direction/evidence/input.json +69 -0
- package/templates/example-direction/evidence/motion.json +33 -0
- package/templates/example-direction/evidence/patch.json +23 -0
- package/templates/example-direction/evidence/rig-config.json +55 -0
- package/templates/example-direction/evidence/still.png +0 -0
- package/templates/example-direction/project.json +372 -0
- package/templates/example-direction/reasoning.txt +1 -0
- package/templates/example-direction/words.txt +1 -0
- package/templates/example-draft/README.md +28 -0
- package/templates/example-draft/config.json +71 -0
- package/templates/example-draft/derived/drafts/lantern/config.json +71 -0
- package/templates/example-draft/derived/drafts/lantern/input/painting_in_rig.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts/background.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts/eye_l.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts/eye_r.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts/face.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts/lantern.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts/torso.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/input/parts.json +188 -0
- package/templates/example-draft/derived/drafts/lantern/input/recomposite_rig.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/interview-requirements.json +8 -0
- package/templates/example-draft/derived/drafts/lantern/out/check/build/run.json +40 -0
- package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.atlas +28 -0
- package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.json +193 -0
- package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.model.json +483 -0
- package/templates/example-draft/derived/drafts/lantern/out/check/build/skeleton.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/images/background.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/images/eye_l.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/images/eye_r.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/images/face.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/images/lantern.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/images/torso.png +0 -0
- package/templates/example-draft/derived/drafts/lantern/out/mesh_report.json +1 -0
- package/templates/example-draft/derived/drafts/lantern/out/motion.json +137 -0
- package/templates/example-draft/derived/drafts/lantern/out/rig.json +102 -0
- package/templates/example-draft/derived/drafts/lantern/out/run.json +52 -0
- package/templates/example-draft/derived/parts/rig/painting_in_rig.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts/background.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts/eye_l.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts/eye_r.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts/face.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts/lantern.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts/torso.png +0 -0
- package/templates/example-draft/derived/parts/rig/parts.json +188 -0
- package/templates/example-draft/derived/parts/rig/recomposite_rig.png +0 -0
- package/templates/example-draft/derived/review/v1/config.json +71 -0
- package/templates/example-draft/derived/review/v1/interview-requirements.json +8 -0
- package/templates/example-draft/derived/review/v1/out/check/build/run.json +40 -0
- package/templates/example-draft/derived/review/v1/out/check/build/skeleton.atlas +28 -0
- package/templates/example-draft/derived/review/v1/out/check/build/skeleton.json +193 -0
- package/templates/example-draft/derived/review/v1/out/check/build/skeleton.model.json +483 -0
- package/templates/example-draft/derived/review/v1/out/check/build/skeleton.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts/background.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts/eye_l.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts/eye_r.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts/face.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts/lantern.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts/torso.png +0 -0
- package/templates/example-draft/derived/review/v1/out/parts.json +188 -0
- package/templates/example-draft/derived/review/v1/out/rig/images/background.png +0 -0
- package/templates/example-draft/derived/review/v1/out/rig/images/eye_l.png +0 -0
- package/templates/example-draft/derived/review/v1/out/rig/images/eye_r.png +0 -0
- package/templates/example-draft/derived/review/v1/out/rig/images/face.png +0 -0
- package/templates/example-draft/derived/review/v1/out/rig/images/lantern.png +0 -0
- package/templates/example-draft/derived/review/v1/out/rig/images/torso.png +0 -0
- package/templates/example-draft/derived/review/v1/out/rig/mesh_report.json +1 -0
- package/templates/example-draft/derived/review/v1/out/rig/motion.json +137 -0
- package/templates/example-draft/derived/review/v1/out/rig/rig.json +102 -0
- package/templates/example-draft/derived/review/v1/out/rig/run.json +52 -0
- package/templates/example-draft/derived/review/v2/config.json +71 -0
- package/templates/example-draft/derived/review/v2/interview-requirements.json +8 -0
- package/templates/example-draft/derived/review/v2/out/check/build/run.json +40 -0
- package/templates/example-draft/derived/review/v2/out/check/build/skeleton.atlas +28 -0
- package/templates/example-draft/derived/review/v2/out/check/build/skeleton.json +193 -0
- package/templates/example-draft/derived/review/v2/out/check/build/skeleton.model.json +483 -0
- package/templates/example-draft/derived/review/v2/out/check/build/skeleton.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts/background.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts/eye_l.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts/eye_r.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts/face.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts/lantern.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts/torso.png +0 -0
- package/templates/example-draft/derived/review/v2/out/parts.json +188 -0
- package/templates/example-draft/derived/review/v2/out/rig/images/background.png +0 -0
- package/templates/example-draft/derived/review/v2/out/rig/images/eye_l.png +0 -0
- package/templates/example-draft/derived/review/v2/out/rig/images/eye_r.png +0 -0
- package/templates/example-draft/derived/review/v2/out/rig/images/face.png +0 -0
- package/templates/example-draft/derived/review/v2/out/rig/images/lantern.png +0 -0
- package/templates/example-draft/derived/review/v2/out/rig/images/torso.png +0 -0
- package/templates/example-draft/derived/review/v2/out/rig/mesh_report.json +1 -0
- package/templates/example-draft/derived/review/v2/out/rig/motion.json +137 -0
- package/templates/example-draft/derived/review/v2/out/rig/rig.json +102 -0
- package/templates/example-draft/derived/review/v2/out/rig/run.json +52 -0
- package/templates/example-draft/evidence/diff-components.json +71 -0
- package/templates/example-draft/evidence/direction/v4.json +64 -0
- package/templates/example-draft/evidence/direction-input-v4.json +64 -0
- package/templates/example-draft/evidence/fix-ledger/lantern.json +17 -0
- package/templates/example-draft/evidence/parts-check.json +127 -0
- package/templates/example-draft/evidence/parts-review.json +88 -0
- package/templates/example-draft/evidence/review-reads/4ed0507381809746443021554e012032be5b5f8417da0f2e525e9b6df16e9f05.json +59 -0
- package/templates/example-draft/evidence/scene/s1.json +65 -0
- package/templates/example-draft/evidence/scene-background.png +0 -0
- package/templates/example-draft/evidence/scene-citations.json +9 -0
- package/templates/example-draft/evidence/structure-decisions.json +9 -0
- package/templates/example-draft/evidence/v1/interview-config.json +71 -0
- package/templates/example-draft/evidence/v1/interview-requirements.json +8 -0
- package/templates/example-draft/evidence/v1/review-config.json +71 -0
- package/templates/example-draft/evidence/v2/interview-config.json +71 -0
- package/templates/example-draft/evidence/v2/interview-requirements.json +8 -0
- package/templates/example-draft/evidence/v2/review-config.json +71 -0
- package/templates/example-draft/input/painting_in_rig.png +0 -0
- package/templates/example-draft/input/parts/background.png +0 -0
- package/templates/example-draft/input/parts/eye_l.png +0 -0
- package/templates/example-draft/input/parts/eye_r.png +0 -0
- package/templates/example-draft/input/parts/face.png +0 -0
- package/templates/example-draft/input/parts/lantern.png +0 -0
- package/templates/example-draft/input/parts/torso.png +0 -0
- package/templates/example-draft/input/parts.json +188 -0
- package/templates/example-draft/input/recomposite_rig.png +0 -0
- package/templates/example-draft/inputs/painting.png +0 -0
- package/templates/example-draft/interview-requirements.json +8 -0
- package/templates/example-draft/out/check/build/run.json +40 -0
- package/templates/example-draft/out/check/build/skeleton.atlas +28 -0
- package/templates/example-draft/out/check/build/skeleton.json +193 -0
- package/templates/example-draft/out/check/build/skeleton.model.json +483 -0
- package/templates/example-draft/out/check/build/skeleton.png +0 -0
- package/templates/example-draft/out/images/background.png +0 -0
- package/templates/example-draft/out/images/eye_l.png +0 -0
- package/templates/example-draft/out/images/eye_r.png +0 -0
- package/templates/example-draft/out/images/face.png +0 -0
- package/templates/example-draft/out/images/lantern.png +0 -0
- package/templates/example-draft/out/images/torso.png +0 -0
- package/templates/example-draft/out/mesh_report.json +1 -0
- package/templates/example-draft/out/motion.json +137 -0
- package/templates/example-draft/out/rig.json +102 -0
- package/templates/example-draft/out/run.json +52 -0
- package/templates/example-draft/outputs/v1/out/check/build/run.json +40 -0
- package/templates/example-draft/outputs/v1/out/check/build/skeleton.atlas +28 -0
- package/templates/example-draft/outputs/v1/out/check/build/skeleton.json +193 -0
- package/templates/example-draft/outputs/v1/out/check/build/skeleton.model.json +483 -0
- package/templates/example-draft/outputs/v1/out/check/build/skeleton.png +0 -0
- package/templates/example-draft/outputs/v1/out/painting_in_rig.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts/background.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts/eye_l.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts/eye_r.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts/face.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts/lantern.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts/torso.png +0 -0
- package/templates/example-draft/outputs/v1/out/parts.json +188 -0
- package/templates/example-draft/outputs/v1/out/rig/images/background.png +0 -0
- package/templates/example-draft/outputs/v1/out/rig/images/eye_l.png +0 -0
- package/templates/example-draft/outputs/v1/out/rig/images/eye_r.png +0 -0
- package/templates/example-draft/outputs/v1/out/rig/images/face.png +0 -0
- package/templates/example-draft/outputs/v1/out/rig/images/lantern.png +0 -0
- package/templates/example-draft/outputs/v1/out/rig/images/torso.png +0 -0
- package/templates/example-draft/outputs/v1/out/rig/mesh_report.json +1 -0
- package/templates/example-draft/outputs/v1/out/rig/motion.json +137 -0
- package/templates/example-draft/outputs/v1/out/rig/rig.json +102 -0
- package/templates/example-draft/outputs/v1/out/rig/run.json +52 -0
- package/templates/example-draft/outputs/v2/out/check/build/run.json +40 -0
- package/templates/example-draft/outputs/v2/out/check/build/skeleton.atlas +28 -0
- package/templates/example-draft/outputs/v2/out/check/build/skeleton.json +193 -0
- package/templates/example-draft/outputs/v2/out/check/build/skeleton.model.json +483 -0
- package/templates/example-draft/outputs/v2/out/check/build/skeleton.png +0 -0
- package/templates/example-draft/outputs/v2/out/painting_in_rig.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts/background.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts/eye_l.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts/eye_r.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts/face.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts/lantern.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts/torso.png +0 -0
- package/templates/example-draft/outputs/v2/out/parts.json +188 -0
- package/templates/example-draft/outputs/v2/out/rig/images/background.png +0 -0
- package/templates/example-draft/outputs/v2/out/rig/images/eye_l.png +0 -0
- package/templates/example-draft/outputs/v2/out/rig/images/eye_r.png +0 -0
- package/templates/example-draft/outputs/v2/out/rig/images/face.png +0 -0
- package/templates/example-draft/outputs/v2/out/rig/images/lantern.png +0 -0
- package/templates/example-draft/outputs/v2/out/rig/images/torso.png +0 -0
- package/templates/example-draft/outputs/v2/out/rig/mesh_report.json +1 -0
- package/templates/example-draft/outputs/v2/out/rig/motion.json +137 -0
- package/templates/example-draft/outputs/v2/out/rig/rig.json +102 -0
- package/templates/example-draft/outputs/v2/out/rig/run.json +52 -0
- package/templates/example-interview-choice.json +1068 -0
- package/templates/example-interview-options/constraint.json +376 -0
- package/templates/example-interview-options/example-assemble.json +376 -0
- package/templates/example-interview-options/example-bones.json +190 -0
- package/templates/example-interview-options/example-camera-depth.json +99 -0
- package/templates/example-interview-options/example-constraints.json +272 -0
- package/templates/example-interview-options/example-meshes.json +190 -0
- package/templates/example-interview-options/example-motion.json +272 -0
- package/templates/example-interview-options/example-picks.json +15 -0
- package/templates/example-interview-options/example-post-composition.json +110 -0
- package/templates/example-interview-options/example-post-timing.json +105 -0
- package/templates/example-interview-options/example-reference.json +63 -0
- package/templates/example-interview-options/example-rig-type.json +63 -0
- package/templates/example-interview-options/example-seethrough.json +190 -0
- package/templates/example-interview-options/fullscreen.json +63 -0
- package/templates/example-interview-options/other-explicit.json +63 -0
- package/templates/example-interview-options/other-pending.json +63 -0
- package/templates/example-interview-options/other-picks.json +16 -0
- package/templates/example-interview.md +213 -0
- package/templates/example-invariants/invariants.json +1339 -0
- package/templates/example-prepared/prepared.svg +1 -0
- package/templates/example-prepared/source.svg +1 -0
- package/templates/example-project.json +4259 -0
- package/templates/example-remesh/AGENTS.md +134 -0
- package/templates/example-remesh/CLAUDE.md +3 -0
- package/templates/example-remesh/evidence/.gitkeep +0 -0
- package/templates/example-remesh/evidence/approval.txt +1 -0
- package/templates/example-remesh/evidence/captions.json +101 -0
- package/templates/example-remesh/evidence/diff-components.json +34 -0
- package/templates/example-remesh/evidence/direction/v4.json +74 -0
- package/templates/example-remesh/evidence/direction/v5.json +74 -0
- package/templates/example-remesh/evidence/direction-input-v4.json +74 -0
- package/templates/example-remesh/evidence/direction-input-v5.json +74 -0
- package/templates/example-remesh/evidence/interview/assemble.json +309 -0
- package/templates/example-remesh/evidence/interview/bones.json +190 -0
- package/templates/example-remesh/evidence/interview/camera_depth.json +103 -0
- package/templates/example-remesh/evidence/interview/constraints.json +272 -0
- package/templates/example-remesh/evidence/interview/meshes.json +190 -0
- package/templates/example-remesh/evidence/interview/motion.json +272 -0
- package/templates/example-remesh/evidence/interview/post_composition.json +123 -0
- package/templates/example-remesh/evidence/interview/post_timing.json +103 -0
- package/templates/example-remesh/evidence/interview/reference.json +59 -0
- package/templates/example-remesh/evidence/interview/rig_type.json +59 -0
- package/templates/example-remesh/evidence/interview/seethrough.json +186 -0
- package/templates/example-remesh/evidence/parts-check.json +127 -0
- package/templates/example-remesh/evidence/parts-review.json +88 -0
- package/templates/example-remesh/evidence/reopen.txt +1 -0
- package/templates/example-remesh/evidence/review-reads/6a17db694db96f1b6049a61a017e0c8e66757b982b8851e0187e561cfcf9af68.json +59 -0
- package/templates/example-remesh/evidence/scene/s1.json +69 -0
- package/templates/example-remesh/evidence/scene-citations.json +9 -0
- package/templates/example-remesh/evidence/setup.png +0 -0
- package/templates/example-remesh/evidence/split-config.json +1 -0
- package/templates/example-remesh/evidence/structure.json +30 -0
- package/templates/example-remesh/evidence/v1/registration/at-source.json +44 -0
- package/templates/example-remesh/evidence/v1/registration/citations.json +68 -0
- package/templates/example-remesh/evidence/v1/registration/compile-report.json +368 -0
- package/templates/example-remesh/evidence/v1/registration/config-source.json +88 -0
- package/templates/example-remesh/evidence/v1/registration/config.json +88 -0
- package/templates/example-remesh/evidence/v1/registration/interview-requirements.json +8 -0
- package/templates/example-remesh/evidence/v1/registration/package-lock.json +11 -0
- package/templates/example-remesh/evidence/v1/registration/path-adapter.json +1 -0
- package/templates/example-remesh/evidence/v1/registration/rigc-run-1.json +44 -0
- package/templates/example-remesh/evidence/v2/registration/at-source.json +44 -0
- package/templates/example-remesh/evidence/v2/registration/citations.json +68 -0
- package/templates/example-remesh/evidence/v2/registration/compile-report.json +368 -0
- package/templates/example-remesh/evidence/v2/registration/config-source.json +88 -0
- package/templates/example-remesh/evidence/v2/registration/config.json +88 -0
- package/templates/example-remesh/evidence/v2/registration/interview-requirements.json +8 -0
- package/templates/example-remesh/evidence/v2/registration/package-lock.json +11 -0
- package/templates/example-remesh/evidence/v2/registration/path-adapter.json +1 -0
- package/templates/example-remesh/evidence/v2/registration/rigc-run-1.json +44 -0
- package/templates/example-remesh/evidence/v3/registration/at-source.json +44 -0
- package/templates/example-remesh/evidence/v3/registration/citations.json +68 -0
- package/templates/example-remesh/evidence/v3/registration/compile-report.json +368 -0
- package/templates/example-remesh/evidence/v3/registration/config-source.json +88 -0
- package/templates/example-remesh/evidence/v3/registration/config.json +88 -0
- package/templates/example-remesh/evidence/v3/registration/interview-requirements.json +8 -0
- package/templates/example-remesh/evidence/v3/registration/package-lock.json +11 -0
- package/templates/example-remesh/evidence/v3/registration/path-adapter.json +1 -0
- package/templates/example-remesh/evidence/v3/registration/rigc-run-1.json +44 -0
- package/templates/example-remesh/inputs/background.png +0 -0
- package/templates/example-remesh/inputs/painting.png +0 -0
- package/templates/example-remesh/inputs/references/.gitkeep +0 -0
- package/templates/example-remesh/package.json +9 -0
- package/templates/example-remesh/project.json +3916 -0
- package/templates/example-reopened/project.json +4568 -0
- package/templates/example-review/config.json +1 -0
- package/templates/example-review/out/check/build/skeleton.model.json +1 -0
- package/templates/example-review/out/parts/piece.svg +1 -0
- package/templates/example-review/out/parts.json +1 -0
- package/templates/example-review/out/rig/mesh_report.json +1 -0
- package/templates/example-review/out/rig/motion.json +1 -0
- package/templates/example-review/out/rig/rig.json +1 -0
- package/templates/example-split/captions.json +357 -0
- package/templates/example-split/config.json +215 -0
- package/templates/legacy-example-project.json +1071 -0
- package/templates/project.json +199 -0
- package/templates/shared/conventions.json +23 -0
package/src/drafts.ts
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { acquireBoundJobWrite, prepareDraftIntent, publishCommittedVersion } from './job-writer.ts';
|
|
3
|
+
import { cancelUncommittedIntent, type PendingPublication } from './job-outbox.ts';
|
|
4
|
+
/** Draft identity is create-owned inventory, never producer provenance or a measurement. */
|
|
5
|
+
import { ConfigInputsUnavailable, configInputBindings, configInputReferences, DIRECTORY_INPUT_RECEIPT, DRAFT_CONFIG_INPUTS_SINCE, draftUnboundInputs, inputsUndeclaredByInstall, receiptSpec, releaseAfter } from './config-inputs.ts';
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { closeSync, fsyncSync, openSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync } from './check-reads.ts';
|
|
9
|
+
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
10
|
+
import { asArray, isObject, type Finding } from './check.ts';
|
|
11
|
+
import { readProject } from './feedback.ts';
|
|
12
|
+
import { diffStackCandidates } from './diff-map.ts';
|
|
13
|
+
import { locateParts } from './parts-sections.ts';
|
|
14
|
+
import { Refusal, refuse, USAGE_EXIT } from './refusal.ts';
|
|
15
|
+
import { RunRecordError, validateRunRecord } from './run-record.ts';
|
|
16
|
+
import { newRefusals } from './refusal-baseline.ts';
|
|
17
|
+
import { readVerbatim } from './record-input.ts';
|
|
18
|
+
import { checkUnrecordedPartRepaints } from './corrections.ts';
|
|
19
|
+
import { checkFixLedger, draftLedgerFields } from './fix-ledger.ts';
|
|
20
|
+
import type { Json, JsonObject } from './templates.ts';
|
|
21
|
+
|
|
22
|
+
const sha = (bytes: Uint8Array): string => createHash('sha256').update(bytes).digest('hex');
|
|
23
|
+
const hex = (v: Json | undefined): v is string => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
|
|
24
|
+
const idPattern = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
25
|
+
const inside = (root: string, path: string): boolean => { const rel = relative(root, path); return rel !== '' && rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel); };
|
|
26
|
+
const pathOf = (dir: string, path: string): string => relative(realpathSync(dir), realpathSync(path)).split(sep).join('/');
|
|
27
|
+
|
|
28
|
+
function fileAt(dir: string, path: string, root: string): string {
|
|
29
|
+
if (isAbsolute(path) || path.split('/').some(p => p === '..' || p === '.' || p === '')) throw new Error(`${path}: expected a project-relative literal path`);
|
|
30
|
+
if (!inside(realpathSync(dir), realpathSync(resolve(dir, root)))) throw new Error(`${root}: draft root escapes the project`);
|
|
31
|
+
const file = resolve(dir, path);
|
|
32
|
+
if (!inside(resolve(dir, root), file) || !inside(realpathSync(resolve(dir, root)), realpathSync(file))) throw new Error(`${path}: outside ${root}`);
|
|
33
|
+
if (!lstatSync(file).isFile()) throw new Error(`${path}: expected a regular file`);
|
|
34
|
+
return file;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function inventory(dir: string, path: string, root: string): JsonObject[] {
|
|
38
|
+
const absolute = resolve(dir, path);
|
|
39
|
+
if (!inside(resolve(dir, root), absolute) && absolute !== resolve(dir, root)) throw new Error(`${path}: outside ${root}`);
|
|
40
|
+
if (!inside(realpathSync(resolve(dir, root)), realpathSync(absolute)) && realpathSync(absolute) !== realpathSync(resolve(dir, root))) throw new Error(`${path}: escapes ${root}`);
|
|
41
|
+
if (!lstatSync(absolute).isDirectory()) throw new Error(`${path}: expected a directory without symlinks`);
|
|
42
|
+
const files: JsonObject[] = [];
|
|
43
|
+
for (const name of readdirSync(absolute).sort()) {
|
|
44
|
+
const entry = join(path, name).split(sep).join('/');
|
|
45
|
+
const info = lstatSync(resolve(dir, entry));
|
|
46
|
+
if (info.isDirectory()) files.push(...inventory(dir, entry, root));
|
|
47
|
+
else files.push({ path: entry, sha256: sha(readFileSync(fileAt(dir, entry, root))) });
|
|
48
|
+
}
|
|
49
|
+
return files;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The raster parts.json itself declares under painting_in_rig, read from the producer's record and never inferred. */
|
|
53
|
+
function declaredPainting(dir: string, path: string, root: string, files: JsonObject[]): string | null {
|
|
54
|
+
if (!files.some(f => f.path === `${path}/parts.json`)) return null;
|
|
55
|
+
let manifest: Json;
|
|
56
|
+
try { manifest = JSON.parse(readFileSync(fileAt(dir, `${path}/parts.json`, root), 'utf8')) as Json; } catch { return null; }
|
|
57
|
+
const painting = isObject(manifest) ? manifest.painting_in_rig : undefined;
|
|
58
|
+
return isObject(painting) && typeof painting.path === 'string' ? `${path}/${painting.path}` : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** parts.json, parts/* and the declared painting; an undeclared or missing file is not carried. */
|
|
62
|
+
function partsInventory(dir: string, path: string, root: string): { files: JsonObject[]; painting: string | null } {
|
|
63
|
+
const all = inventory(dir, path, root);
|
|
64
|
+
const painting = declaredPainting(dir, path, root, all);
|
|
65
|
+
return { files: all.filter(f => f.path === `${path}/parts.json` || String(f.path).startsWith(`${path}/parts/`) || f.path === painting), painting };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function binding(v: Json | undefined): v is JsonObject {
|
|
69
|
+
return isObject(v) && typeof v.path === 'string' && hex(v.sha256);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** parts' rig-run/1 stamp sits beside the rig.json it names: `<out>/run.json` of `rig --out`, which is `<root>/rig/run.json` in a build root. */
|
|
73
|
+
const stampBeside = (rigJson: string): string => rigJson.replace(/rig\.json$/, 'run.json');
|
|
74
|
+
|
|
75
|
+
/** The producer a rig-run/1 stamp names, read from its record and never recomputed; any other spec is not parts' rig stamp. */
|
|
76
|
+
function stampProducer(bytes: Buffer, path: string): JsonObject {
|
|
77
|
+
const { spec, record } = validateRunRecord(bytes, path);
|
|
78
|
+
if (spec !== 'rig-run/1') throw new RunRecordError('RUN_RECORD_SPEC_UNKNOWN', `${path}.spec`, `expected the rig-run/1 stamp parts writes beside rig.json, found ${spec}`);
|
|
79
|
+
const pkg = record.package as JsonObject;
|
|
80
|
+
return { name: pkg.name as string, version: pkg.version as string };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Every name on the way must be a real directory, and the last a non-symlink; returns whether a name is absent. */
|
|
84
|
+
function absentAlong(dir: string, path: string): boolean {
|
|
85
|
+
const names = path.split('/');
|
|
86
|
+
let current = realpathSync(dir);
|
|
87
|
+
for (const [i, name] of names.entries()) {
|
|
88
|
+
current = join(current, name);
|
|
89
|
+
let info;
|
|
90
|
+
try { info = lstatSync(current); } catch (error) {
|
|
91
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
if (info.isSymbolicLink() || (i < names.length - 1 && !info.isDirectory())) throw new Error(`${path}: draft path traverses a symlink or non-directory`);
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Absence may be regenerable; symlinks, escaped paths and present wrong kinds are never absence. */
|
|
100
|
+
export function draftFileUnavailable(dir: string, path: string, root: string): boolean {
|
|
101
|
+
if (isAbsolute(path) || path.split('/').some(p => p === '..' || p === '.' || p === '')) throw new Error(`${path}: expected a literal project-relative draft path`);
|
|
102
|
+
const target = resolve(dir, path);
|
|
103
|
+
if (target !== resolve(dir, root) && !inside(resolve(dir, root), target)) throw new Error(`${path}: outside ${root}`);
|
|
104
|
+
return absentAlong(dir, path);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A config-named input binds wherever it sits inside the project; one under the draft root is a draft artifact. */
|
|
108
|
+
const projectInput = (dir: string, path: string, root: string): boolean => !inside(resolve(dir, root), resolve(dir, path));
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Availability of a `config_inputs` binding. Under the draft root it is a regenerable draft artifact;
|
|
112
|
+
* elsewhere in the project it is a project input, never regenerable, so its absence throws.
|
|
113
|
+
*/
|
|
114
|
+
export function draftInputUnavailable(dir: string, path: string, root: string): boolean {
|
|
115
|
+
if (!projectInput(dir, path, root)) return draftFileUnavailable(dir, path, root);
|
|
116
|
+
inputAt(dir, path);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A project input outside the draft root: a literal path to a regular file inside the project, also after symlinks. */
|
|
121
|
+
function inputAt(dir: string, path: string): string {
|
|
122
|
+
if (isAbsolute(path) || path.split('/').some(p => p === '..' || p === '.' || p === '')) throw new Error(`${path}: expected a project-relative literal path`);
|
|
123
|
+
const file = resolve(dir, path);
|
|
124
|
+
if (!inside(resolve(dir), file)) throw new Error(`${path}: outside the project`);
|
|
125
|
+
if (absentAlong(dir, path)) throw new Error(`${path}: config-named project input is missing`);
|
|
126
|
+
if (!inside(realpathSync(dir), realpathSync(file))) throw new Error(`${path}: outside the project`);
|
|
127
|
+
if (!lstatSync(file).isFile()) throw new Error(`${path}: expected a regular file`);
|
|
128
|
+
return file;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The bytes a draft binding names: its own artifacts under the draft root, config-named inputs anywhere in the project. */
|
|
132
|
+
const bindingAt = (dir: string, path: string, root: string, input: boolean): string => input && projectInput(dir, path, root) ? inputAt(dir, path) : fileAt(dir, path, root);
|
|
133
|
+
|
|
134
|
+
export function checkDrafts(dir: string, project: JsonObject, refusals: Finding[], warnings: Finding[] = []): void {
|
|
135
|
+
const add = (code: string, path: string, why: string): void => { refusals.push({ code, path, message: `${path}: ${why}; retain a labelled draft with its original config, parts and files` }); };
|
|
136
|
+
if (project.drafts !== undefined && !Array.isArray(project.drafts)) { add('DRAFT_RIG_MALFORMED', 'drafts', 'expected a list'); return; }
|
|
137
|
+
const ids = new Set<string>();
|
|
138
|
+
for (const [index, raw] of asArray(project.drafts).entries()) {
|
|
139
|
+
const path = `drafts[${index}]`;
|
|
140
|
+
if (!isObject(raw) || typeof raw.id !== 'string' || !idPattern.test(raw.id) || ids.has(raw.id) || raw.kind !== 'draft-rig'
|
|
141
|
+
|| typeof raw.at !== 'string' || !Number.isFinite(Date.parse(raw.at)) || !isObject(raw.producer)
|
|
142
|
+
|| typeof raw.producer.name !== 'string' || !raw.producer.name || typeof raw.producer.version !== 'string' || !raw.producer.version
|
|
143
|
+
|| !binding(raw.config) || !isObject(raw.parts) || typeof raw.parts.path !== 'string'
|
|
144
|
+
|| !Array.isArray(raw.parts.files) || !Array.isArray(raw.files)) { add('DRAFT_RIG_MALFORMED', path, 'invalid or duplicate draft identity'); continue; }
|
|
145
|
+
ids.add(raw.id);
|
|
146
|
+
const root = `derived/drafts/${raw.id}`;
|
|
147
|
+
const parts = raw.parts;
|
|
148
|
+
const all = [raw.config, ...asArray(parts.files), ...asArray(raw.files), ...asArray(raw.config_inputs)];
|
|
149
|
+
const inputs = new Set(asArray(raw.config_inputs));
|
|
150
|
+
if (raw.config_inputs !== undefined && (!Array.isArray(raw.config_inputs) || raw.config_inputs.some(f => !binding(f)))) { add('DRAFT_RIG_MALFORMED', path, 'config_inputs must bind paths and sha256 hashes'); continue; }
|
|
151
|
+
if (asArray(raw.config_inputs).some(f => isObject(f) && f.directory !== undefined && (typeof f.directory !== 'string' || f.path !== `${f.directory}/${DIRECTORY_INPUT_RECEIPT}` || typeof f.spec !== 'string' || !f.spec))) { add('DRAFT_RIG_MALFORMED', path, `a config_inputs directory binding must bind <directory>/${DIRECTORY_INPUT_RECEIPT} and its receipt spec`); continue; }
|
|
152
|
+
if (raw.files.length === 0 || asArray(parts.files).length === 0 || all.some(f => !binding(f))
|
|
153
|
+
|| !asArray(parts.files).some(f => binding(f) && f.path === `${String(parts.path)}/parts.json`)) { add('DRAFT_RIG_MALFORMED', path, 'file inventories must bind config, parts.json and output'); continue; }
|
|
154
|
+
const rigs = asArray(raw.files).filter(f => binding(f) && String(f.path).endsWith('/rig.json'));
|
|
155
|
+
if (rigs.length !== 1 || !['motion.json', 'mesh_report.json'].every(name => asArray(raw.files).some(f => binding(f) && f.path === String((rigs[0] as JsonObject).path).replace(/rig\.json$/, name)))) { add('DRAFT_RIG_MALFORMED', path, 'expected one complete rig output layout'); continue; }
|
|
156
|
+
const unavailable = new Set<string>();
|
|
157
|
+
for (const entry of all) {
|
|
158
|
+
if (!binding(entry)) continue;
|
|
159
|
+
const file = String(entry.path);
|
|
160
|
+
try {
|
|
161
|
+
const input = inputs.has(entry);
|
|
162
|
+
if (input ? draftInputUnavailable(dir, file, root) : draftFileUnavailable(dir, file, root)) { unavailable.add(file); continue; }
|
|
163
|
+
const bytes = readFileSync(bindingAt(dir, file, root, input));
|
|
164
|
+
if (sha(bytes) !== entry.sha256) throw new Error(`${file} changed`);
|
|
165
|
+
// A directory input's receipt is read, not re-verified: its declared spec must be the one recorded.
|
|
166
|
+
if (input && entry.directory !== undefined && receiptSpec(bytes) !== entry.spec) throw new Error(`${file} declares spec ${JSON.stringify(receiptSpec(bytes))}, not the recorded ${JSON.stringify(entry.spec)}`);
|
|
167
|
+
} catch (error) { add('DRAFT_RIG_FILE', path, error instanceof Error ? error.message : String(error)); }
|
|
168
|
+
}
|
|
169
|
+
const stamp = stampBeside(String((rigs[0] as JsonObject).path));
|
|
170
|
+
const stampBinding = asArray(raw.files).find(f => binding(f) && f.path === stamp) as JsonObject | undefined;
|
|
171
|
+
const recorded = `${raw.producer.name}@${raw.producer.version}`;
|
|
172
|
+
if (stampBinding === undefined) {
|
|
173
|
+
warnings.push({ code: 'DRAFT_PRODUCER_ASSUMED', path: `${path}.producer`, message: `${path}.producer: no rig-run/1 run.json is bound at ${stamp}, so ${recorded} is the installed package assumed when the draft was recorded, not read from its run; rebuild the draft with parts rig, which writes run.json beside rig.json, and record it with draft-rig under a new id; the old record is kept` });
|
|
174
|
+
} else if (!unavailable.has(stamp)) {
|
|
175
|
+
try {
|
|
176
|
+
const bytes = readFileSync(fileAt(dir, stamp, root));
|
|
177
|
+
if (sha(bytes) === stampBinding.sha256) {
|
|
178
|
+
const read = stampProducer(bytes, stamp);
|
|
179
|
+
if (read.name !== raw.producer.name || read.version !== raw.producer.version) {
|
|
180
|
+
warnings.push({ code: 'DRAFT_PRODUCER_MISMATCH', path: `${path}.producer`, message: `${path}.producer: records ${recorded} but its bound ${stamp} names ${String(read.name)}@${String(read.version)}; record the draft again with draft-rig under a new id; the old record is kept` });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error instanceof RunRecordError) warnings.push({ code: error.code, path: `${path}.producer`, message: `${path}.producer: ${error.path}: ${error.why}; the recorded producer was not read from it; rebuild the draft with parts rig and record it with draft-rig under a new id; the old record is kept` });
|
|
185
|
+
// Unreadable, escaping or changed bytes are DRAFT_RIG_FILE above; the producer is not read from them.
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const unbound = draftUnboundInputs(raw);
|
|
189
|
+
const unrecorded = (why: string): void => { warnings.push({ code: 'DRAFT_CONFIG_INPUTS_UNRECORDED', path: `${path}.config_inputs`, message: `${path}.config_inputs: ${why}; the draft was recorded before ${DRAFT_CONFIG_INPUTS_SINCE} with no config_inputs key, so the parts install that recorded it could not declare its config-named inputs; the record is kept; record a new draft with draft-rig under a new id to bind them` }); };
|
|
190
|
+
try {
|
|
191
|
+
if (!draftFileUnavailable(dir, String(raw.config.path), root)) {
|
|
192
|
+
const config = JSON.parse(readFileSync(fileAt(dir, String(raw.config.path), root), 'utf8'));
|
|
193
|
+
for (const ref of configInputReferences(dir, config)) {
|
|
194
|
+
const named = relative(resolve(dir), resolve(dir, String(raw.config.path), '..', ref.value)).split(sep).join('/');
|
|
195
|
+
// A directory input (an occluder plate) is bound by its receipt and names the directory it stands for.
|
|
196
|
+
if (!asArray(raw.config_inputs).some(f => binding(f) && (f.directory === undefined ? f.path === named : f.directory === named))) {
|
|
197
|
+
if (unbound === 'unrecorded') { unrecorded(`config.${ref.at} names ${named}`); continue; }
|
|
198
|
+
const finding = { code: 'DRAFT_RIG_FILE', path, message: `${path}.config_inputs: missing config.${ref.at} input ${named}; preserve the old draft and record a new draft with draft-rig` };
|
|
199
|
+
(unbound === 'legacy' ? warnings : refusals).push(finding);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (error instanceof ConfigInputsUnavailable && unbound === 'legacy') warnings.push({ code: 'DRAFT_RIG_FILE', path, message: `${path}: ${error.message}` });
|
|
205
|
+
else if (error instanceof ConfigInputsUnavailable && unbound === 'unrecorded') unrecorded(error.message);
|
|
206
|
+
else add('DRAFT_RIG_FILE', path, error instanceof Error ? error.message : String(error));
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
if (!draftFileUnavailable(dir, String(parts.path), root)) {
|
|
210
|
+
const expected = new Map(asArray(parts.files).filter(binding).map(f => [String(f.path), f.sha256]));
|
|
211
|
+
const { files, painting } = partsInventory(dir, String(parts.path), root);
|
|
212
|
+
for (const actual of files) {
|
|
213
|
+
if (painting !== null && actual.path === painting && !expected.has(painting)) {
|
|
214
|
+
warnings.push({ code: 'DRAFT_DECLARED_FILE_UNBOUND', path: `${path}.parts.files`, message: `${path}.parts.files: parts.json declares ${painting} under painting_in_rig but the draft does not bind it; rebuild the draft and record it again with draft-rig` });
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (expected.get(String(actual.path)) !== actual.sha256) throw new Error(`parts inventory changed at ${String(actual.path)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch (error) { add('DRAFT_RIG_FILE', path, error instanceof Error ? error.message : String(error)); }
|
|
221
|
+
if (unavailable.size > 0) warnings.push({ code: 'DRAFT_RIG_UNAVAILABLE', path, message: `${path}: regenerable draft artifacts are unavailable (${[...unavailable].join(', ')}); restore the recorded bytes before inspecting or rebuilding this draft` });
|
|
222
|
+
}
|
|
223
|
+
checkUnrecordedPartRepaints(dir, project, refusals);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Check declared identity/paths, including real paths; identical model bytes alone prove nothing. */
|
|
227
|
+
export function draftVersionFindings(dir: string, project: JsonObject): Finding[] {
|
|
228
|
+
const out: Finding[] = [];
|
|
229
|
+
const reserved = resolve(realpathSync(dir), 'derived/drafts');
|
|
230
|
+
const declared = new Set(asArray(project.drafts).filter(isObject).flatMap(d => [d.config, ...asArray(d.files), ...(isObject(d.parts) ? asArray(d.parts.files) : [])])
|
|
231
|
+
.filter(binding).map(f => String(f.path)));
|
|
232
|
+
const visit = (value: Json, key = ''): boolean => {
|
|
233
|
+
if (typeof value === 'string' && ['path', 'source_dir', 'from'].includes(key)) {
|
|
234
|
+
const absolute = resolve(dir, value);
|
|
235
|
+
if (declared.has(value) || absolute === reserved || inside(reserved, absolute)) return true;
|
|
236
|
+
try { const real = realpathSync(absolute); if (real === reserved || inside(reserved, real)) return true; } catch { /* Other readers report missing files. */ }
|
|
237
|
+
}
|
|
238
|
+
if (Array.isArray(value)) return value.some(v => visit(v));
|
|
239
|
+
return isObject(value) && Object.entries(value).some(([k, v]) => visit(v, k));
|
|
240
|
+
};
|
|
241
|
+
for (const [i, raw] of asArray(project.versions).entries()) {
|
|
242
|
+
if (!isObject(raw)) continue;
|
|
243
|
+
if (raw.kind === 'draft-rig' || raw.draft === true || raw.draft_id !== undefined || visit(raw)) {
|
|
244
|
+
const path = `versions[${i}]`;
|
|
245
|
+
out.push({ code: 'DRAFT_RIG_AS_VERSION', path, message: `${path}: a draft cannot be reviewed, packaged or handed off as a production version; build a separately authorised production rig` });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const DRAFT_RIG_HELP = `draft-rig:
|
|
252
|
+
<id> [<dir>] --config <path> --from <output dir> [--parts <dir>] [--ledger-report <file>] [--known-regression <row id>=<reason file>]... [--json]
|
|
253
|
+
Record existing labelled draft artifacts under derived/drafts/<id>/; run no producer.
|
|
254
|
+
Standalone rig output requires --parts; only an unambiguous build root supplies it implicitly.
|
|
255
|
+
Config/output association is declared, not measured reproducibility or producer provenance.
|
|
256
|
+
--ledger-report binds the project's own fix-ledger check report (rig-create/fix-ledger-report@1) by sha256;
|
|
257
|
+
a row the previous draft passed that does not pass here is refused unless named by --known-regression.`;
|
|
258
|
+
|
|
259
|
+
export function draftRigCommand(args: string[], now: number): { stdout: string; exit: number } {
|
|
260
|
+
const opts: Record<string, string> = {};
|
|
261
|
+
const positional: string[] = [];
|
|
262
|
+
const known: [string, string][] = [];
|
|
263
|
+
let json = false;
|
|
264
|
+
for (let i = 0; i < args.length; i++) {
|
|
265
|
+
const arg = args[i] as string;
|
|
266
|
+
if (arg === '--json') json = true;
|
|
267
|
+
else if (arg === '--known-regression') {
|
|
268
|
+
const value = args[++i];
|
|
269
|
+
const split = value === undefined ? -1 : value.indexOf('=');
|
|
270
|
+
const row = split > 0 ? value!.slice(0, split) : '';
|
|
271
|
+
if (!idPattern.test(row) || split === value!.length - 1 || known.some(([id]) => id === row)) refuse('DRAFT_RIG_USAGE', 'drafts: --known-regression needs <row id>=<reason file>, once per row; see draft-rig help', USAGE_EXIT);
|
|
272
|
+
known.push([row, value!.slice(split + 1)]);
|
|
273
|
+
} else if (['--config', '--from', '--parts', '--ledger-report'].includes(arg)) {
|
|
274
|
+
const value = args[++i];
|
|
275
|
+
if (!value || value.startsWith('--') || opts[arg] !== undefined) refuse('DRAFT_RIG_USAGE', `drafts: ${arg} requires one value; see draft-rig help`, USAGE_EXIT);
|
|
276
|
+
opts[arg] = value;
|
|
277
|
+
} else if (arg.startsWith('-')) refuse('DRAFT_RIG_USAGE', `drafts: unsupported ${arg}; see draft-rig help`, USAGE_EXIT);
|
|
278
|
+
else positional.push(arg);
|
|
279
|
+
}
|
|
280
|
+
const id = positional[0];
|
|
281
|
+
if (!id || !idPattern.test(id) || positional.length > 2 || !opts['--config'] || !opts['--from']) refuse('DRAFT_RIG_USAGE', DRAFT_RIG_HELP, USAGE_EXIT);
|
|
282
|
+
if (known.length && !opts['--ledger-report']) refuse('DRAFT_RIG_USAGE', 'drafts: --known-regression names rows of a --ledger-report; supply the report', USAGE_EXIT);
|
|
283
|
+
const reasons: [string, string][] = known.map(([row, file]) => {
|
|
284
|
+
const reason = readVerbatim(file).trim();
|
|
285
|
+
if (!reason) refuse('DRAFT_RIG_USAGE', `drafts: the --known-regression reason file for ${row} is empty; say why the regression is accepted`, USAGE_EXIT);
|
|
286
|
+
return [row, reason];
|
|
287
|
+
});
|
|
288
|
+
const dir = resolve(positional[1] ?? process.cwd());
|
|
289
|
+
const release = acquireBoundJobWrite(dir);
|
|
290
|
+
let intent: PendingPublication | null = null;
|
|
291
|
+
const temporary = join(dir, `.draft-rig-record-${randomUUID()}.tmp`);
|
|
292
|
+
try {
|
|
293
|
+
const { file } = readProject(dir);
|
|
294
|
+
const original = readFileSync(file);
|
|
295
|
+
const project = JSON.parse(original.toString('utf8')) as JsonObject;
|
|
296
|
+
const before = structuredClone(project);
|
|
297
|
+
const root = `derived/drafts/${id}`;
|
|
298
|
+
let record: JsonObject;
|
|
299
|
+
try {
|
|
300
|
+
const from = opts['--from'];
|
|
301
|
+
const output = inventory(dir, from, root);
|
|
302
|
+
const has = (name: string): boolean => output.some(f => f.path === `${from}/${name}`);
|
|
303
|
+
const build = has('parts.json') && output.some(f => String(f.path).startsWith(`${from}/parts/`)) && has('rig/rig.json');
|
|
304
|
+
const standalone = has('rig.json');
|
|
305
|
+
if (build === standalone) throw new Error('ambiguous or missing build/standalone rig layout');
|
|
306
|
+
if (standalone && !opts['--parts']) throw new Error('standalone rig output needs --parts');
|
|
307
|
+
const parts = opts['--parts'] ?? from;
|
|
308
|
+
const partsFiles = partsInventory(dir, parts, root).files;
|
|
309
|
+
const candidates = diffStackCandidates(resolve(dir, parts, 'parts.json'));
|
|
310
|
+
const present = candidates.filter(file => existsSync(file));
|
|
311
|
+
if (present.length !== 1) refuse('DRAFT_RIG_STACK', `drafts[${id}].parts.path: ${parts}/parts.json has ${present.length === 0 ? 'no stack candidate' : 'ambiguous stack candidates'} (${candidates.map(file => relative(dir, file)).join(', ')}); re-run rig-parts build into the draft output, or keep assemble's render/ beside rig/`);
|
|
312
|
+
fileAt(dir, relative(dir, present[0]!).split(sep).join('/'), root);
|
|
313
|
+
const rigPrefix = build ? `${from}/rig` : from;
|
|
314
|
+
for (const name of ['rig.json', 'motion.json', 'mesh_report.json']) if (!output.some(f => f.path === `${rigPrefix}/${name}`)) throw new Error(`${name}: missing rig output`);
|
|
315
|
+
const configFile = fileAt(dir, opts['--config'], root);
|
|
316
|
+
const config = JSON.parse(readFileSync(configFile, 'utf8')) as Json;
|
|
317
|
+
if (!isObject(config)) throw new Error('config must be an object');
|
|
318
|
+
const installed = locateParts(dir);
|
|
319
|
+
if (installed === null) throw new Error('install @rig-c/parts before recording a draft');
|
|
320
|
+
const pkgFile = join(installed, 'package.json');
|
|
321
|
+
const pkg = JSON.parse(readFileSync(pkgFile, 'utf8')) as { name: string; version: string };
|
|
322
|
+
const stamp = stampBeside(`${rigPrefix}/rig.json`);
|
|
323
|
+
let producer: JsonObject = { name: pkg.name, version: pkg.version };
|
|
324
|
+
if (output.some(f => f.path === stamp)) {
|
|
325
|
+
try { producer = stampProducer(readFileSync(fileAt(dir, stamp, root)), stamp); } catch (error) {
|
|
326
|
+
if (error instanceof RunRecordError) refuse(error.code, `drafts[${id}]: ${error.path}: ${error.why}; re-run parts rig into the draft output so it writes run.json with its outputs, then record the draft`);
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
// A producer newer than the install may declare inputs the install cannot; recording without them would leave the key absent.
|
|
330
|
+
if (releaseAfter(String(producer.version), pkg.version)) {
|
|
331
|
+
const undeclared = inputsUndeclaredByInstall(dir, config);
|
|
332
|
+
if (undeclared.refs.length) refuse('DRAFT_PARTS_INSTALL_BEHIND', `drafts[${id}].config_inputs: the project's @rig-c/parts ${undeclared.version} cannot declare config-named inputs (${undeclared.refs.map(ref => `config.${ref.at}`).join(', ')}) that its producer @rig-c/parts ${String(producer.version)}, read from ${stamp}, declares; install @rig-c/parts ${String(producer.version)} in the project, then record the draft again`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Named inputs bind wherever they sit inside the project; checkDrafts below reads them as check does.
|
|
336
|
+
const configInputs = configInputBindings(dir, opts['--config']);
|
|
337
|
+
record = { id, kind: 'draft-rig', at: new Date(now).toISOString(), producer, config: { path: pathOf(dir, configFile), sha256: sha(readFileSync(configFile)) }, parts: { path: parts, files: partsFiles }, files: output, ...(configInputs.length ? { config_inputs: configInputs } : {}) };
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (error instanceof Refusal) throw error;
|
|
340
|
+
refuse('DRAFT_RIG_MALFORMED', `drafts[${id}]: ${error instanceof Error ? error.message : String(error)}; retain an unambiguous labelled output and its --parts inputs`);
|
|
341
|
+
}
|
|
342
|
+
if (project.drafts !== undefined && !Array.isArray(project.drafts)) refuse('DRAFT_RIG_MALFORMED', 'drafts: expected a list; restore the original record');
|
|
343
|
+
project.drafts = [...asArray(project.drafts), record];
|
|
344
|
+
if (opts['--ledger-report']) Object.assign(record, draftLedgerFields(dir, project, asArray(project.drafts).length - 1, opts['--ledger-report'], reasons, now));
|
|
345
|
+
const errors: Finding[] = [];
|
|
346
|
+
checkDrafts(dir, project, errors);
|
|
347
|
+
const ledger: Finding[] = [];
|
|
348
|
+
checkFixLedger(dir, project, ledger, [], now);
|
|
349
|
+
const index = asArray(project.drafts).length - 1;
|
|
350
|
+
const first = errors[0] ?? ledger.find(f => f.path === `drafts[${index}]` || f.path.startsWith(`drafts[${index}].`)) ?? newRefusals(dir, before, project, { now })[0];
|
|
351
|
+
if (first !== undefined) refuse(first.code, first.message);
|
|
352
|
+
const bytes = Buffer.from(`${JSON.stringify(project, null, 2)}\n`);
|
|
353
|
+
if (!readFileSync(file).equals(original)) refuse('DRAFT_RIG_FILE', 'project.json: draft source changed during registration; retry with frozen inputs');
|
|
354
|
+
intent = prepareDraftIntent(dir, bytes, id);
|
|
355
|
+
const fd = openSync(temporary, 'wx');
|
|
356
|
+
try { writeFileSync(fd, bytes); fsyncSync(fd); } finally { closeSync(fd); }
|
|
357
|
+
renameSync(temporary, file);
|
|
358
|
+
const directory = openSync(dir, 'r');
|
|
359
|
+
try { fsyncSync(directory); } finally { closeSync(directory); }
|
|
360
|
+
publishCommittedVersion(dir, id);
|
|
361
|
+
return { stdout: json ? JSON.stringify(record, null, 2) : `draft-rig: recorded ${id}; no production version created`, exit: 0 };
|
|
362
|
+
} finally { cancelUncommittedIntent(dir, intent); rmSync(temporary, { force: true }); release(); }
|
|
363
|
+
}
|