@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.
- package/LICENSE +21 -0
- package/README.md +169 -2
- package/docs/CHECKS.md +1682 -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/amend.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { readFileSync, realpathSync } from './check-reads.ts';
|
|
4
|
+
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { asArray, asString, collectSignoffs, isObject, type Finding } from './check.ts';
|
|
7
|
+
import { readProject } from './feedback.ts';
|
|
8
|
+
import { readVerbatim } from './record-input.ts';
|
|
9
|
+
import { refuse, USAGE_EXIT } from './refusal.ts';
|
|
10
|
+
import { newRefusals } from './refusal-baseline.ts';
|
|
11
|
+
import { liveSignoff, type Reopen } from './reopen.ts';
|
|
12
|
+
import { stageIndex, stageOfStep } from './stages.ts';
|
|
13
|
+
import type { JsonObject } from './templates.ts';
|
|
14
|
+
|
|
15
|
+
/** Fixed approval-kind → step-0 gap mapping. These are missing proofs, not inferred dependencies. */
|
|
16
|
+
export const AMENDMENT_APPROVAL_GAPS: Readonly<Record<string, string>> = {
|
|
17
|
+
interview: 'G1',
|
|
18
|
+
'plan-approved': 'G2',
|
|
19
|
+
parts: 'G3',
|
|
20
|
+
'parts-review': 'G5',
|
|
21
|
+
'rigging-interview': 'G2',
|
|
22
|
+
rigging: 'G3',
|
|
23
|
+
'rig-review': 'G7',
|
|
24
|
+
'post-interview': 'G2',
|
|
25
|
+
post: 'G3',
|
|
26
|
+
'direction-review': 'G7',
|
|
27
|
+
accepted: 'G9',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export interface Amendment extends Reopen {
|
|
31
|
+
readonly index: number;
|
|
32
|
+
readonly target: JsonObject;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AmendmentApproval {
|
|
36
|
+
readonly path: string;
|
|
37
|
+
readonly stage: string;
|
|
38
|
+
readonly checkpoint?: string;
|
|
39
|
+
readonly reason: string;
|
|
40
|
+
readonly gap?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AmendmentSummary {
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly target: JsonObject;
|
|
46
|
+
readonly lapsed: AmendmentApproval[];
|
|
47
|
+
readonly upstream: AmendmentApproval[];
|
|
48
|
+
readonly keep_by_binding: AmendmentApproval[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Future binding cards may populate this list with independently proved approvals. Ships empty. */
|
|
52
|
+
export function keepByBinding(): AmendmentApproval[] {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Only well-formed human amendments can invalidate approvals. Historical bindings name recorded bytes. */
|
|
57
|
+
export function collectAmendments(project: JsonObject, refusals: Finding[]): Amendment[] {
|
|
58
|
+
const out: Amendment[] = [];
|
|
59
|
+
const invalid = (code: string, path: string, why: string): void => {
|
|
60
|
+
refusals.push({ code, path, message: `${path}: ${why}; record a human amend with an artifact, its owning stage and new input hashes` });
|
|
61
|
+
};
|
|
62
|
+
if (project.amendments !== undefined && !Array.isArray(project.amendments)) {
|
|
63
|
+
invalid('AMENDMENT_MALFORMED', 'amendments', 'must be a list');
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
for (const [index, raw] of asArray(project.amendments).entries()) {
|
|
67
|
+
const path = `amendments[${index}]`;
|
|
68
|
+
if (!isObject(raw) || ['by', 'at', 'verbatim'].some(k => asString(raw[k]) === null)
|
|
69
|
+
|| !(raw.by as string).trim() || !Number.isFinite(Date.parse(raw.at as string))
|
|
70
|
+
|| (raw.source !== undefined && typeof raw.source !== 'string')) {
|
|
71
|
+
invalid('AMENDMENT_MALFORMED', path, 'needs by, a valid at, verbatim and optional text source');
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if ((raw.by as string).trim().toLowerCase() === 'agent') {
|
|
75
|
+
invalid('SIGNOFF_NOT_HUMAN', path, 'signed by agent');
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const target = raw.target;
|
|
79
|
+
if (!isObject(target) || !['part', 'config-field', 'motion-track', 'interview-answer'].includes(String(target.kind))
|
|
80
|
+
|| typeof target.id !== 'string' || !target.id.trim() || typeof target.stage !== 'string'
|
|
81
|
+
|| target.stage === 'accepted' || !Object.hasOwn(AMENDMENT_APPROVAL_GAPS, target.stage)) {
|
|
82
|
+
invalid('AMENDMENT_TARGET_INVALID', `${path}.target`, 'needs kind, non-empty artifact id and a non-terminal owning stage');
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(raw.changes) || raw.changes.length === 0 || raw.changes.some(c => !isObject(c)
|
|
86
|
+
|| typeof c.path !== 'string' || !c.path.trim() || typeof c.sha256 !== 'string' || !/^[0-9a-f]{64}$/.test(c.sha256))
|
|
87
|
+
|| new Set(raw.changes.map(c => (c as JsonObject).path)).size !== raw.changes.length) {
|
|
88
|
+
invalid('AMENDMENT_CHANGES_INVALID', `${path}.changes`, 'needs distinct new input paths and lowercase SHA-256 hashes');
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
out.push({ index, target, stage: target.stage, at: Date.parse(raw.at as string) });
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Name each recorded approval act. Re-signatures remain separate acts in history. */
|
|
97
|
+
export function amendmentSummaries(project: JsonObject, amendments: readonly Amendment[], events: readonly Reopen[]): AmendmentSummary[] {
|
|
98
|
+
const approvals = collectSignoffs(project, []).map(s => ({
|
|
99
|
+
path: `signoffs[${s.index}]`, stage: s.stage, at: s.at,
|
|
100
|
+
...(s.checkpoint === null ? {} : { checkpoint: s.checkpoint }),
|
|
101
|
+
}));
|
|
102
|
+
for (const [vi, raw] of asArray(project.versions).entries()) {
|
|
103
|
+
if (!isObject(raw)) continue;
|
|
104
|
+
for (const [di, verdict] of asArray(raw.verdicts).entries()) {
|
|
105
|
+
if (!isObject(verdict) || verdict.verdict !== 'accepted' || typeof verdict.by !== 'string'
|
|
106
|
+
|| verdict.by.toLowerCase() === 'agent' || !['rig-review', 'direction-review'].includes(String(verdict.review))) continue;
|
|
107
|
+
approvals.push({ path: `versions[${vi}].verdicts[${di}]`, stage: String(verdict.review), at: Date.parse(String(verdict.at)) });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return amendments.map(a => {
|
|
111
|
+
const lapsed: AmendmentApproval[] = [];
|
|
112
|
+
const upstream: AmendmentApproval[] = [];
|
|
113
|
+
for (const approval of approvals) {
|
|
114
|
+
if (approval.at > a.at || stageIndex(approval.stage) < 0) continue;
|
|
115
|
+
const named = { path: approval.path, stage: approval.stage, ...('checkpoint' in approval ? { checkpoint: approval.checkpoint } : {}) };
|
|
116
|
+
if (stageIndex(approval.stage) >= stageIndex(a.stage)) {
|
|
117
|
+
lapsed.push({ ...named, reason: 'no binding proves independence', gap: AMENDMENT_APPROVAL_GAPS[approval.stage] });
|
|
118
|
+
} else if (liveSignoff(events, approval.stage, approval.at)) {
|
|
119
|
+
upstream.push({ ...named, reason: 'stage precedes amended stage' });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { path: `amendments[${a.index}]`, target: a.target, lapsed, upstream, keep_by_binding: keepByBinding() };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const AMEND_HELP = `amend:
|
|
127
|
+
<stage> [<project dir>] record a forward amendment, retaining project.stage
|
|
128
|
+
--kind <kind> part, config-field, motion-track or interview-answer
|
|
129
|
+
--target <id> artifact id, config pointer, track id or answer path
|
|
130
|
+
--change <file> new input under the project, hashed by the writer (repeatable)
|
|
131
|
+
--by <who> human signer (agent is refused)
|
|
132
|
+
--verbatim-file <file> unchanged UTF-8 words, or - for stdin (default)
|
|
133
|
+
--source <text> optional origin, separate from verbatim
|
|
134
|
+
--json print the amendment record
|
|
135
|
+
The clock supplies at. The owning stage and every later approval lapse.
|
|
136
|
+
Upstream steps keep their state. keep-by-binding ships empty.`;
|
|
137
|
+
|
|
138
|
+
export function amendCommand(args: string[], now: number): { stdout: string; exit: number } {
|
|
139
|
+
const opts: Record<string, string> = {};
|
|
140
|
+
const positional: string[] = [];
|
|
141
|
+
const files: string[] = [];
|
|
142
|
+
let json = false;
|
|
143
|
+
for (let i = 0; i < args.length; i++) {
|
|
144
|
+
const arg = args[i] as string;
|
|
145
|
+
if (arg === '--json') json = true;
|
|
146
|
+
else if (['--kind', '--target', '--change', '--by', '--verbatim-file', '--source'].includes(arg)) {
|
|
147
|
+
const value = args[++i];
|
|
148
|
+
if (value === undefined || !value.trim() || (arg !== '--change' && arg in opts)) refuse('AMENDMENT_OPTION_INVALID', `amendments: ${arg} needs one non-empty value; see amend help`, USAGE_EXIT);
|
|
149
|
+
if (arg === '--change') files.push(value);
|
|
150
|
+
else opts[arg] = value;
|
|
151
|
+
} else if (arg.startsWith('-')) refuse('UNKNOWN_OPTION', `amendments: unsupported ${arg}; see amend help`, USAGE_EXIT);
|
|
152
|
+
else positional.push(arg);
|
|
153
|
+
}
|
|
154
|
+
if (positional.length < 1 || positional.length > 2 || opts['--by'] === undefined || opts['--kind'] === undefined || opts['--target'] === undefined || files.length === 0) {
|
|
155
|
+
refuse('AMENDMENT_OPTION_INVALID', 'amendments: supply an owning stage, --kind, --target, --change and --by; see amend help', USAGE_EXIT);
|
|
156
|
+
}
|
|
157
|
+
const dir = resolve(positional[1] ?? process.cwd());
|
|
158
|
+
const { file, project } = readProject(dir);
|
|
159
|
+
const before = structuredClone(project);
|
|
160
|
+
const errors: Finding[] = [];
|
|
161
|
+
collectAmendments(project, errors);
|
|
162
|
+
if (errors[0] !== undefined) refuse(errors[0].code, errors[0].message);
|
|
163
|
+
const entry: JsonObject = {
|
|
164
|
+
target: { stage: positional[0] as string, kind: opts['--kind'], id: opts['--target'] },
|
|
165
|
+
by: opts['--by'], at: new Date(now).toISOString(), verbatim: '', changes: [],
|
|
166
|
+
};
|
|
167
|
+
if (opts['--source'] !== undefined) entry.source = opts['--source'];
|
|
168
|
+
// Check signer and target before reading stdin or any input file.
|
|
169
|
+
entry.changes = files.map(path => ({ path, sha256: '0'.repeat(64) }));
|
|
170
|
+
const entryErrors: Finding[] = [];
|
|
171
|
+
collectAmendments({ amendments: [entry] }, entryErrors);
|
|
172
|
+
if (entryErrors[0] !== undefined) refuse(entryErrors[0].code, entryErrors[0].message);
|
|
173
|
+
entry.changes = files.map(path => {
|
|
174
|
+
try {
|
|
175
|
+
const canonical = realpathSync(resolve(dir, path));
|
|
176
|
+
const local = relative(realpathSync(dir), canonical).split(sep).join('/');
|
|
177
|
+
if (isAbsolute(path) || local === '' || local === '..' || local.startsWith('../')) throw new Error('new inputs must stay under the project');
|
|
178
|
+
return { path: local, sha256: createHash('sha256').update(readFileSync(canonical)).digest('hex') };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
refuse('AMENDMENT_INPUT_UNREAD', `amendments.changes ${path}: ${error instanceof Error ? error.message : String(error)}; retain the new input under the project and amend again`);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
entry.verbatim = readVerbatim(opts['--verbatim-file'] ?? '-');
|
|
184
|
+
project.amendments = [...asArray(project.amendments), entry];
|
|
185
|
+
for (const raw of asArray(isObject(project.plan) ? project.plan.steps : undefined)) {
|
|
186
|
+
if (isObject(raw) && typeof raw.id === 'string' && (raw.phase === 'rig' || raw.phase === 'post')
|
|
187
|
+
&& stageIndex(stageOfStep(raw.phase, raw.id)) >= stageIndex(positional[0] as string)) raw.status = 'pending';
|
|
188
|
+
}
|
|
189
|
+
const rejected = newRefusals(dir, before, project, { now })[0];
|
|
190
|
+
if (rejected !== undefined) refuse(rejected.code, rejected.message);
|
|
191
|
+
writeFileSync(file, `${JSON.stringify(project, null, 2)}\n`);
|
|
192
|
+
return { stdout: json ? JSON.stringify(entry, null, 2) : `amend: ${String(positional[0])} ${String(opts['--target'])} by ${String(entry.by)} at ${String(entry.at)}; stage retained`, exit: 0 };
|
|
193
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { retainedReviewRead, reviewReadLegacy, reviewSessionSource, selectedReviewFeedback, ReviewReadError } from './review-read.ts';
|
|
2
|
+
import { configInputsLegacy } from './config-inputs.ts';
|
|
3
|
+
/** A retained review Apply and the create version made from it (create#65). */
|
|
4
|
+
import { isAbsolute, relative, resolve, sep, posix } from 'node:path';
|
|
5
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
6
|
+
|
|
7
|
+
import type { Json } from './templates.ts';
|
|
8
|
+
import { asArray, isObject, type Ctx } from './check.ts';
|
|
9
|
+
import { reviewConfigInputs, reviewHandoffInputs } from './review-package.ts';
|
|
10
|
+
|
|
11
|
+
export const APPLIED_REVIEW_SINCE = '2026-11-06T00:00:00Z';
|
|
12
|
+
const hex = (value: unknown): value is string => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
13
|
+
const inside = (root: string, path: string): boolean => {
|
|
14
|
+
const rel = relative(root, path);
|
|
15
|
+
return rel !== '' && rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Missing old declarations warn, while an explicit null records a build made without feedback. */
|
|
19
|
+
export function checkAppliedReview(ctx: Pick<Ctx, 'dir' | 'project' | 'refusals' | 'warnings'>): void {
|
|
20
|
+
const versions = asArray(ctx.project.versions).filter(isObject);
|
|
21
|
+
for (const version of versions) {
|
|
22
|
+
if (version.pre_interview === true || !Number.isSafeInteger(version.n)) continue;
|
|
23
|
+
const path = `versions[v${String(version.n)}].review_origin`;
|
|
24
|
+
const push = (code: string, message: string): void => { ctx.refusals.push({ code, path, message: `${path}: ${message}` }); };
|
|
25
|
+
const record = version.review_origin;
|
|
26
|
+
if (record === undefined) {
|
|
27
|
+
const at = typeof version.at === 'string' ? Date.parse(version.at) : Number.NaN;
|
|
28
|
+
const legacy = !Number.isFinite(at) || at < Date.parse(APPLIED_REVIEW_SINCE);
|
|
29
|
+
const finding = { code: legacy ? 'APPLIED_REVIEW_LEGACY' : 'APPLIED_REVIEW_MISSING', path, message: `${path}: declare null for a build made without feedback, or { feedback: {path, sha256}, source_version, feedback_id, applied }; records before ${APPLIED_REVIEW_SINCE} retain their old contract` };
|
|
30
|
+
(legacy ? ctx.warnings : ctx.refusals).push(finding);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (record === null) continue;
|
|
34
|
+
if (isObject(record) && record.review_read === undefined && reviewReadLegacy(version.at)) {
|
|
35
|
+
ctx.warnings.push({ code: 'REVIEW_READ_LEGACY', path, message: `${path}: historical applied review has no CLI response receipt; preserve history and query new results through rig-review feedback --format json` });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!isObject(record) || !isObject(record.feedback) || typeof record.feedback.path !== 'string'
|
|
39
|
+
|| isAbsolute(record.feedback.path) || !inside(resolve(ctx.dir), resolve(ctx.dir, record.feedback.path))
|
|
40
|
+
|| !hex(record.feedback.sha256) || !Number.isSafeInteger(record.source_version)
|
|
41
|
+
|| typeof record.feedback_id !== 'string' || !record.feedback_id.trim() || !isObject(record.applied)
|
|
42
|
+
|| typeof record.applied.schema !== 'string' || !record.applied.schema.startsWith('rig-review-applied/')
|
|
43
|
+
|| !hex(record.applied.configHash) || !hex(record.applied.modelHash)) {
|
|
44
|
+
push('APPLIED_REVIEW_MALFORMED', 'expected feedback {project-relative path, sha256}, source_version, feedback_id and the complete context.applied object');
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const earlier = versions.filter((v) => v.kind === version.kind && typeof v.n === 'number' && v.n < (version.n as number));
|
|
48
|
+
const source = earlier.reduce<(typeof versions)[number] | undefined>((latest, v) => latest === undefined || (v.n as number) > (latest.n as number) ? v : latest, undefined);
|
|
49
|
+
if (source === undefined || source.n !== record.source_version || versions.filter((v) => v.n === record.source_version).length !== 1) {
|
|
50
|
+
push('APPLIED_REVIEW_SOURCE', 'source_version must name the immediately preceding version of the same kind, without skipping a link');
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
let feedback: Json;
|
|
54
|
+
try {
|
|
55
|
+
feedback = retainedReviewRead(ctx.dir,record.review_read,record.feedback);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
push(error instanceof ReviewReadError ? error.code : 'APPLIED_REVIEW_FILE', error instanceof Error ? error.message : String(error));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const reviewed = isObject(feedback) ? reviewSessionSource(feedback) : null;
|
|
61
|
+
if (!isObject(feedback) || reviewed === null
|
|
62
|
+
|| feedback.schema === 'rig-review-session/1' && !Array.isArray(feedback.feedback)) {
|
|
63
|
+
push('APPLIED_REVIEW_FILE', 'expected the single-build JSON written by rig-review feedback');
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const pkg = source.review_package;
|
|
67
|
+
const handoff = isObject(reviewed.handoff) ? reviewed.handoff : null;
|
|
68
|
+
const files = isObject(pkg) ? asArray(pkg.files) : [];
|
|
69
|
+
const fileHash = (name: string): Json | undefined => files.find((f) => isObject(f) && f.path === name);
|
|
70
|
+
const model = fileHash('out/check/build/skeleton.model.json');
|
|
71
|
+
let inputs: ReturnType<typeof reviewHandoffInputs> = [];
|
|
72
|
+
let named: ReturnType<typeof reviewConfigInputs> = [];
|
|
73
|
+
try {
|
|
74
|
+
if (isObject(pkg)) {
|
|
75
|
+
const issue = (reason: string, at: unknown): void => {
|
|
76
|
+
if (!configInputsLegacy(at)) throw new Error(reason);
|
|
77
|
+
ctx.warnings.push({ code: 'APPLIED_REVIEW_SOURCE', path, message: `${path}: ${reason}; preserve history and include the config inputs on the next build` });
|
|
78
|
+
};
|
|
79
|
+
named = reviewConfigInputs(pkg, ctx.dir, Number(source.n), name => issue(`config-named input ${name} is missing from source package`, source.at), error => issue(error.message, version.at));
|
|
80
|
+
inputs = reviewHandoffInputs(pkg, ctx.dir, Number(source.n), named);
|
|
81
|
+
}
|
|
82
|
+
} catch (error) { push('APPLIED_REVIEW_SOURCE', `${error instanceof Error ? error.message : String(error)}; restore the source config and its named input bindings`); continue; }
|
|
83
|
+
const handoffFiles = isObject(handoff) ? asArray(handoff.files) : [];
|
|
84
|
+
const missingNamed = named.filter(input => hex(input.sha256) && !handoffFiles.some(f => isObject(f) && typeof f.path === 'string' && posix.normalize(`out/check/${f.path}`) === input.path));
|
|
85
|
+
if (missingNamed.length && configInputsLegacy(version.at)) ctx.warnings.push({ code: 'APPLIED_REVIEW_SOURCE', path, message: `${path}: legacy handoff omits config-named inputs ${missingNamed.map(f => f.path).join(', ')}; preserve history and include them in the next rebuild` });
|
|
86
|
+
const requiredInputs = inputs.filter(f => !named.some(n => n.path === f.path && (!hex(n.sha256) || configInputsLegacy(version.at))));
|
|
87
|
+
const boundInputs = requiredInputs.every((input) => isObject(input) && handoffFiles.some((f) => isObject(f)
|
|
88
|
+
&& typeof f.path === 'string' && posix.normalize(`out/check/${f.path}`) === input.path && f.sha256 === input.sha256));
|
|
89
|
+
if (!isObject(pkg) || !isObject(pkg.config) || !isObject(handoff?.config)
|
|
90
|
+
|| handoff.config.sha256 !== pkg.config.sha256 || record.applied.configHash !== pkg.config.sha256
|
|
91
|
+
|| !isObject(model) || !isObject(reviewed.rig) || reviewed.rig.path !== 'build/skeleton.model.json'
|
|
92
|
+
|| reviewed.rig.sha256 !== model.sha256 || reviewed.motion !== null || !boundInputs
|
|
93
|
+
|| !handoffFiles.every(f => isObject(f) && typeof f.path === 'string' && inputs.some(input => hex(input.sha256) && posix.normalize(`out/check/${f.path}`) === input.path && f.sha256 === input.sha256)) || new Set(handoffFiles.map(f => isObject(f) ? f.path : null)).size !== handoffFiles.length || asArray(handoff.missing).length !== 0) {
|
|
94
|
+
push('APPLIED_REVIEW_SOURCE', `feedback handoff inputs and inspected compiled model must match the source version package${named.length ? `; config-named inputs ${named.map(f => f.path).join(', ')} must carry their bound hashes` : ''}; include the source config inputs when rebuilding`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const items = selectedReviewFeedback(feedback, record.feedback_id as string);
|
|
98
|
+
const item = items[0];
|
|
99
|
+
if (items.length !== 1 || !isObject(item) || !isObject(item.context) || !isObject(item.context.applied)
|
|
100
|
+
|| !isDeepStrictEqual(record.applied, item.context.applied)) {
|
|
101
|
+
push('APPLIED_REVIEW_RESULT', 'feedback_id must select exactly one feedback item with the same non-null context.applied');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { asArray, isDecision, isObject, type Ctx } from './check.ts';
|
|
2
|
+
import { assembleAnswer, checkAssembleAnswers, PROJECTION_ASKS_SINCE } from './assemble-answers.ts';
|
|
3
|
+
import { approvalStageOfGuide, hasStageOrder } from './stages.ts';
|
|
4
|
+
import { WAYPOINT_ASKS } from './waypoints.ts';
|
|
5
|
+
|
|
6
|
+
/** Independent of other contract dates; read the resolved guide's own at. */
|
|
7
|
+
export const ASK_COVERAGE_SINCE = '2026-11-05T00:00:00Z';
|
|
8
|
+
|
|
9
|
+
export function checkAskCoverage(ctx: Ctx): void {
|
|
10
|
+
const assemble = checkAssembleAnswers(ctx);
|
|
11
|
+
const rigType = ctx.items.find((item) => item.id === 'rig_type')?.state;
|
|
12
|
+
const rigValue = rigType?.state === 'user' ? rigType.answer.value
|
|
13
|
+
: rigType?.state === 'delegated' && isObject(rigType.answer.agent_decision) ? rigType.answer.agent_decision.value : null;
|
|
14
|
+
for (const item of ctx.items) {
|
|
15
|
+
if (item.kind !== 'waypoint' || (item.state.state !== 'user' && item.state.state !== 'delegated')) continue;
|
|
16
|
+
const answer = item.state.answer;
|
|
17
|
+
const asks = WAYPOINT_ASKS[item.id]?.filter((ask) => (ask.id !== 'ground' || item.id !== 'assemble' || assemble.visible)
|
|
18
|
+
&& (ask.required_for === undefined || (ask.required_for === 'fullscreen.game_logic' ? rigValue === 'fullscreen.game_logic' : rigValue !== 'fullscreen.game_logic')));
|
|
19
|
+
if (asks === undefined) continue;
|
|
20
|
+
const path = `${item.path}.asks`;
|
|
21
|
+
if (!('asks' in answer)) {
|
|
22
|
+
const at = typeof answer.at === 'string' ? Date.parse(answer.at) : Number.NaN;
|
|
23
|
+
const legacy = !Number.isFinite(at) || at < Date.parse(ASK_COVERAGE_SINCE);
|
|
24
|
+
if (legacy) {
|
|
25
|
+
ctx.warnings.push({ code: 'GUIDE_ASKS_LEGACY', path, message: `${path}: no per-ask record; guide predates ${ASK_COVERAGE_SINCE} or has no readable at; preserve the legacy answer` });
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const entries = asArray(answer.asks);
|
|
30
|
+
const ids = new Set(asks.map((ask) => ask.id));
|
|
31
|
+
const seen = new Set<string>();
|
|
32
|
+
let malformed = !Array.isArray(answer.asks) && 'asks' in answer;
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!isObject(entry) || typeof entry.id !== 'string' || (!ids.has(entry.id) && !(isObject(answer.choice) && item.id === 'assemble' && entry.id === 'ground')) || seen.has(entry.id)
|
|
35
|
+
|| (entry.state !== 'user' && entry.state !== 'delegated' && !(isObject(answer.choice) && entry.state === 'other-pending'))
|
|
36
|
+
|| (entry.state === 'user' && item.state.state !== 'user')) {
|
|
37
|
+
malformed = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
seen.add(entry.id);
|
|
41
|
+
}
|
|
42
|
+
if (malformed) ctx.refusals.push({ code: 'GUIDE_ASKS_MALFORMED', path, message: `${path}: use one { id, state: 'user' | 'delegated' } per applicable ask, with known, unique ids; a full delegation cannot claim user answers` });
|
|
43
|
+
for (const ask of asks) {
|
|
44
|
+
const projectionAsk = item.id === 'assemble' && (ask.id === 'projection' || ask.id === 'ground');
|
|
45
|
+
if (projectionAsk && assemble.legacy && !seen.has(ask.id)) {
|
|
46
|
+
ctx.warnings.push({ code: 'GUIDE_ASKS_LEGACY', path: `${path}[${ask.id}]`, message: `${item.id}: '${ask.id}' predates ${PROJECTION_ASKS_SINCE}; preserve the old guide and ask when it is next revised` });
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const value = projectionAsk ? assembleAnswer(answer, ask.id === 'projection' ? 'project' : 'ground') : undefined;
|
|
50
|
+
const valid = !projectionAsk || (ask.id === 'projection' ? value === 'core' || value === 'visible' : value === 'plain' || value === 'busy');
|
|
51
|
+
const pending = isObject(answer.choice) && entries.some(e => isObject(e) && (e.state === 'other-pending' || (e.id === ask.id && e.state === 'delegated' && !isDecision(answer.agent_decision))));
|
|
52
|
+
if (!seen.has(ask.id) || (!valid && !pending)) ctx.refusals.push({ code: 'GUIDE_ASK_UNANSWERED', path: `${path}[${ask.id}]`, message: `${item.id}: unanswered ask '${ask.id}': ${ask.text} Record its user answer or explicit delegation.` });
|
|
53
|
+
}
|
|
54
|
+
const partialDelegation = item.state.state === 'user' && entries.some((entry) => isObject(entry) && entry.state === 'delegated');
|
|
55
|
+
if (partialDelegation) {
|
|
56
|
+
const decision = answer.agent_decision;
|
|
57
|
+
if (!isObject(decision) || typeof decision.covers !== 'string' || decision.covers.trim() === '') {
|
|
58
|
+
if (!ctx.refusals.some((finding) => finding.code === 'GUIDE_PARTIAL_UNMARKED' && finding.path === item.path)) {
|
|
59
|
+
ctx.refusals.push({ code: 'GUIDE_PARTIAL_UNMARKED', path: item.path, message: `${item.path}: delegated asks need agent_decision.covers naming the remainder` });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const approved = ctx.signoffs.some((signoff) => signoff.stage === approvalStageOfGuide(item.id, false, hasStageOrder(ctx.project)) && signoff.checkpoint === null);
|
|
63
|
+
if (approved && !isDecision(decision)
|
|
64
|
+
&& !ctx.refusals.some((finding) => finding.code === 'DELEGATION_UNDECIDED' && finding.path === item.path)) {
|
|
65
|
+
ctx.refusals.push({ code: 'DELEGATION_UNDECIDED', path: item.path, message: `${item.path}: delegated asks need agent_decision.value and reasoning before plan approval` });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync } from './check-reads.ts';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { asArray, asString, isObject, itemFor, type Ctx } from './check.ts';
|
|
5
|
+
import { readObject } from './cg-gates.ts';
|
|
6
|
+
import type { Json, JsonObject } from './templates.ts';
|
|
7
|
+
|
|
8
|
+
/** New asks apply to guides made under the new contract. */
|
|
9
|
+
export const PROJECTION_ASKS_SINCE = '2026-11-06T00:00:00Z';
|
|
10
|
+
|
|
11
|
+
/** A partial delegation can decide either field beside the user's own value. */
|
|
12
|
+
export function assembleAnswer(answer: JsonObject, field: 'project' | 'ground'): Json | undefined {
|
|
13
|
+
const entry = asArray(answer.asks).find((raw) => isObject(raw) && raw.id === (field === 'project' ? 'projection' : 'ground'));
|
|
14
|
+
if (!isObject(entry)) return undefined;
|
|
15
|
+
const value = entry.state === 'user' ? answer.value
|
|
16
|
+
: entry.state === 'delegated' && isObject(answer.agent_decision) ? answer.agent_decision.value : undefined;
|
|
17
|
+
return isObject(value) ? value[field] : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Read the current config and retained version configs with the existing hash/path checks. */
|
|
21
|
+
export function checkAssembleAnswers(ctx: Ctx): { visible: boolean; legacy: boolean } {
|
|
22
|
+
const item = itemFor(ctx, 'assemble');
|
|
23
|
+
const state = item?.state;
|
|
24
|
+
const answer = state?.state === 'user' || state?.state === 'delegated' ? state.answer : {};
|
|
25
|
+
const at = typeof answer.at === 'string' ? Date.parse(answer.at) : Number.NaN;
|
|
26
|
+
const legacy = !Number.isFinite(at) || at < Date.parse(PROJECTION_ASKS_SINCE);
|
|
27
|
+
let visible = assembleAnswer(answer, 'project') === 'visible';
|
|
28
|
+
const configs: { path: string; sha: string | null; at: string }[] = [];
|
|
29
|
+
if (existsSync(join(ctx.dir, 'config.json'))) configs.push({ path: 'config.json', sha: null, at: 'config.json' });
|
|
30
|
+
asArray(ctx.project.versions).forEach((raw, i) => {
|
|
31
|
+
if (!isObject(raw) || raw.kind !== 'rig' || !isObject(raw.provenance) || !isObject(raw.provenance.config)) return;
|
|
32
|
+
const config = raw.provenance.config;
|
|
33
|
+
const path = asString(config.path);
|
|
34
|
+
if (path !== null) configs.push({ path, sha: asString(config.sha256), at: `versions[${typeof raw.n === 'number' ? `v${raw.n}` : i}].provenance.config` });
|
|
35
|
+
});
|
|
36
|
+
for (const config of configs) {
|
|
37
|
+
const read = readObject(ctx.dir, resolve(ctx.dir, config.path), config.sha);
|
|
38
|
+
// Missing retained files already have provenance checks; this check introduces no new core refusal.
|
|
39
|
+
if (!read.ok) continue;
|
|
40
|
+
const assemble = isObject(read.value.assemble) ? read.value.assemble : {};
|
|
41
|
+
if (assemble.project !== 'visible') continue;
|
|
42
|
+
visible = true;
|
|
43
|
+
const ground = assembleAnswer(answer, 'ground');
|
|
44
|
+
const pendingChoice = isObject(answer.choice) && asArray(answer.asks).some(a => isObject(a) && a.state === 'other-pending');
|
|
45
|
+
const decision = isObject(answer.agent_decision) ? answer.agent_decision : {};
|
|
46
|
+
const pendingGround = isObject(answer.choice)
|
|
47
|
+
&& asArray(answer.asks).some(a => isObject(a) && a.id === 'ground' && a.state === 'delegated')
|
|
48
|
+
&& !asArray(decision.fragments).some(f => isObject(f) && f.ask === 'ground');
|
|
49
|
+
if (legacy || pendingChoice || pendingGround || ground === 'plain' || ground === 'busy') continue;
|
|
50
|
+
ctx.refusals.push({ code: 'ASSEMBLE_GROUND_UNANSWERED', path: `${config.at} assemble.ground`, message: `${config.path}: assemble.project is visible with no ground answer; ask plain or busy, record the assemble guide's ground ask and value (or explicit decided delegation), then write assemble.ground in the config.` });
|
|
51
|
+
}
|
|
52
|
+
return { visible, legacy };
|
|
53
|
+
}
|
package/src/cg-gates.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CG gates (docs/CHECKS.md § CG gates): a rig version of a full-screen
|
|
3
|
+
* scene (an illustration CG brought to life, `rig_type` `fullscreen.*`) is held
|
|
4
|
+
* to the three opt-in `@rig-c/parts` gates that measure what a CG build is for —
|
|
5
|
+
* every part's pixels trace to the split or to a receipted edit, no seam shows
|
|
6
|
+
* background in motion, no part boundary carries a colour step the pieces do
|
|
7
|
+
* not have:
|
|
8
|
+
*
|
|
9
|
+
* - `assemble.editor_receipts.trace_parts: true` in the version's config;
|
|
10
|
+
* - `seam_over_motion.required: true` and
|
|
11
|
+
* - `boundary_colour.max_step_excess` (an integer, at most
|
|
12
|
+
* {@link CG_BOUNDARY_ALLOWANCE}) in the requirements file the config names
|
|
13
|
+
* as `requirements`, so `rig-parts build` runs both from the config alone.
|
|
14
|
+
*
|
|
15
|
+
* create writes no config: the agent does. So the declarations are not
|
|
16
|
+
* written here; this check refuses, by name, a CG version whose config does
|
|
17
|
+
* not carry them, the same for every CG project. It reads the files; what the
|
|
18
|
+
* gates measure is parts' (`rig-parts build` / `check`), not create's.
|
|
19
|
+
*
|
|
20
|
+
* `rig_type` is also checked against the values docs/INTERVIEW.md lists, since
|
|
21
|
+
* a value outside them would decide nothing here and let a CG build through.
|
|
22
|
+
*/
|
|
23
|
+
import { createHash } from 'node:crypto';
|
|
24
|
+
import { existsSync, readFileSync } from './check-reads.ts';
|
|
25
|
+
import { dirname, relative, resolve, sep } from 'node:path';
|
|
26
|
+
|
|
27
|
+
import { asString, isObject, itemFor, asArray, type Ctx } from './check.ts';
|
|
28
|
+
import { validConfigLoss } from './pre-interview.ts';
|
|
29
|
+
import type { Json, JsonObject } from './templates.ts';
|
|
30
|
+
|
|
31
|
+
/** The `rig_type` values (docs/INTERVIEW.md § Framing). */
|
|
32
|
+
export const RIG_TYPES: readonly string[] = ['character.idle', 'character.complex', 'character.monster', 'fullscreen.minimal', 'fullscreen.game_logic'];
|
|
33
|
+
|
|
34
|
+
/** A `rig_type` with this prefix is a full-screen scene: create's notion of a CG build. */
|
|
35
|
+
export const CG_RIG_TYPE_PREFIX = 'fullscreen.';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The boundary colour allowance a CG version declares at most: parts' public
|
|
39
|
+
* examples read 1 to 4 clean and 21 and up planted (parts AUTHORING,
|
|
40
|
+
* *Boundary colour step*), and each declares 12. A lower value is stricter and
|
|
41
|
+
* allowed; a higher one is refused.
|
|
42
|
+
*/
|
|
43
|
+
export const CG_BOUNDARY_ALLOWANCE = 12;
|
|
44
|
+
|
|
45
|
+
/** The first `@rig-c/parts` with all three gates (issues #969, #975, #976). A version built with an earlier one could not declare them. */
|
|
46
|
+
export const CG_GATES_FROM_PARTS = '2.4.0';
|
|
47
|
+
|
|
48
|
+
function versionLabel(v: JsonObject, index: number): string {
|
|
49
|
+
return typeof v.n === 'number' ? `versions[v${v.n}]` : `versions[${index}]`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function semver(v: string): [number, number, number] | null {
|
|
53
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v);
|
|
54
|
+
return m === null ? null : [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function before(a: string, b: string): boolean {
|
|
58
|
+
const x = semver(a);
|
|
59
|
+
const y = semver(b);
|
|
60
|
+
if (x === null || y === null) return false;
|
|
61
|
+
for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return (x[i] as number) < (y[i] as number);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The answered `rig_type` value: the user's `value`, or the agent's decision on a delegation. */
|
|
66
|
+
function rigTypeValue(ctx: Ctx): { value: Json | undefined; path: string } | null {
|
|
67
|
+
const item = itemFor(ctx, 'rig_type');
|
|
68
|
+
if (item === undefined) return null;
|
|
69
|
+
const s = item.state;
|
|
70
|
+
// A pending Other has no type yet; the choice gate refuses its interview/plan approval.
|
|
71
|
+
if (s.state === 'user' && s.answer.value === null && isObject(s.answer.choice) && asArray(s.answer.asks).some(a => isObject(a) && a.state === 'other-pending')) return null;
|
|
72
|
+
if (s.state === 'user') return { value: s.answer.value, path: item.path };
|
|
73
|
+
if (s.state === 'delegated' && s.decided && isObject(s.answer.agent_decision)) return { value: s.answer.agent_decision.value, path: item.path };
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type Read = { ok: true; value: JsonObject; file: string } | { ok: false; why: string };
|
|
78
|
+
|
|
79
|
+
function inside(dir: string, file: string): boolean {
|
|
80
|
+
const rel = relative(dir, file);
|
|
81
|
+
return rel !== '' && !rel.startsWith('..') && !rel.startsWith(sep);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function readObject(dir: string, file: string, sha256: string | null): Read {
|
|
85
|
+
if (!inside(dir, file)) return { ok: false, why: `${relative(dir, file)} is not under the project` };
|
|
86
|
+
if (!existsSync(file)) return { ok: false, why: `${relative(dir, file)} is not there` };
|
|
87
|
+
let bytes: Buffer;
|
|
88
|
+
try {
|
|
89
|
+
bytes = readFileSync(file);
|
|
90
|
+
} catch {
|
|
91
|
+
return { ok: false, why: `${relative(dir, file)} cannot be read` };
|
|
92
|
+
}
|
|
93
|
+
if (sha256 !== null) {
|
|
94
|
+
const actual = createHash('sha256').update(bytes).digest('hex');
|
|
95
|
+
if (actual !== sha256) return { ok: false, why: `${relative(dir, file)} hashes to ${actual}, not the recorded ${sha256}` };
|
|
96
|
+
}
|
|
97
|
+
let parsed: unknown;
|
|
98
|
+
try {
|
|
99
|
+
parsed = JSON.parse(bytes.toString('utf8'));
|
|
100
|
+
} catch {
|
|
101
|
+
return { ok: false, why: `${relative(dir, file)} is not JSON` };
|
|
102
|
+
}
|
|
103
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return { ok: false, why: `${relative(dir, file)} is not a JSON object` };
|
|
104
|
+
return { ok: true, value: parsed as JsonObject, file };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Refuses an unknown `rig_type` and, on a CG project, every rig version whose config does not declare the three gates. */
|
|
108
|
+
export function checkCgGates(ctx: Ctx, recordOnly = false): void {
|
|
109
|
+
const rigType = rigTypeValue(ctx);
|
|
110
|
+
if (rigType === null) return; // unanswered or not applicable: reported by the answer checks
|
|
111
|
+
if (typeof rigType.value !== 'string' || !RIG_TYPES.includes(rigType.value)) {
|
|
112
|
+
ctx.refusals.push({
|
|
113
|
+
code: 'RIG_TYPE_UNKNOWN',
|
|
114
|
+
path: rigType.path,
|
|
115
|
+
message: `${rigType.path}: value ${JSON.stringify(rigType.value ?? null)} is none of ${RIG_TYPES.join(', ')}; the value decides which prompts apply and whether the build is a CG (fullscreen.*), so a value outside the list decides nothing`,
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// The answer domain belongs to record asks; build declarations do not.
|
|
120
|
+
if (recordOnly || !rigType.value.startsWith(CG_RIG_TYPE_PREFIX)) return;
|
|
121
|
+
|
|
122
|
+
asArray(ctx.project.versions).forEach((v, index) => {
|
|
123
|
+
if (!isObject(v) || v.kind !== 'rig') return;
|
|
124
|
+
const label = versionLabel(v, index);
|
|
125
|
+
const configLost = validConfigLoss(v);
|
|
126
|
+
if (configLost) {
|
|
127
|
+
ctx.warnings.push({
|
|
128
|
+
code: 'CG_GATES_CONFIG_LOST',
|
|
129
|
+
path: `${label}.config_lost`,
|
|
130
|
+
message: `${label}.config_lost: the historical config was lost, so its CG gates cannot be read; preserve the note and source, and build and accept a new rig with its retained config before post work`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
const builtWith = isObject(v.built_with) ? asString(v.built_with['@rig-c/parts']) : null;
|
|
134
|
+
if (builtWith !== null && before(builtWith, CG_GATES_FROM_PARTS)) {
|
|
135
|
+
ctx.warnings.push({
|
|
136
|
+
code: 'CG_GATES_PREDATE',
|
|
137
|
+
path: `${label}.built_with`,
|
|
138
|
+
message: `${label}: built with @rig-c/parts ${builtWith}, before the CG gates (${CG_GATES_FROM_PARTS}); it is not held to them, and the next CG version declares them`,
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (configLost) return;
|
|
143
|
+
const prov = isObject(v.provenance) ? v.provenance : {};
|
|
144
|
+
const config = isObject(prov.config) ? prov.config : {};
|
|
145
|
+
const configPath = asString(config.path);
|
|
146
|
+
const at = `${label}.provenance.config`;
|
|
147
|
+
if (configPath === null) {
|
|
148
|
+
// A migrated pre-interview version without provenance.config is what `version config-lost` accepts (src/config-lost.ts).
|
|
149
|
+
const lost = v.pre_interview === true && (prov.config === null || prov.config === undefined)
|
|
150
|
+
? `; if this pre-interview version's config did not survive, record the loss with rig-create version config-lost ${String(v.n)} --note <text> --source <text>` : '';
|
|
151
|
+
ctx.refusals.push({ code: 'CG_GATES_UNREAD', path: at, message: `${at}: no path; a CG version's gates are read from the config it was built from — keep it under the project and record its path and sha256${lost}` });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const configSha = asString(config.sha256);
|
|
155
|
+
if (configSha === null || configSha === '') {
|
|
156
|
+
ctx.refusals.push({ code: 'CG_GATES_UNREAD', path: at, message: `${at}: no sha256; a CG version's gates are read from the config it was built from, verified by its recorded hash — record the config's sha256` });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const cfg = readObject(ctx.dir, resolve(ctx.dir, configPath), configSha);
|
|
160
|
+
if (!cfg.ok) {
|
|
161
|
+
ctx.refusals.push({ code: 'CG_GATES_UNREAD', path: at, message: `${at}: ${cfg.why}; a CG version's gates are read from the config it was built from — keep it in evidence/v<N>/ (in git; outputs/ is ignored) at the recorded path and sha256` });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const assemble = isObject(cfg.value.assemble) ? cfg.value.assemble : {};
|
|
165
|
+
const receipts = isObject(assemble.editor_receipts) ? assemble.editor_receipts : {};
|
|
166
|
+
if (receipts.trace_parts !== true) {
|
|
167
|
+
ctx.refusals.push({
|
|
168
|
+
code: 'CG_GATE_UNDECLARED',
|
|
169
|
+
path: `${at} assemble.editor_receipts.trace_parts`,
|
|
170
|
+
message: `${label}: ${configPath} does not declare assemble.editor_receipts.trace_parts: true; a CG build's every part traces to a layer of the split or a receipted edit (parts ASSEMBLE_PART_SOURCE)`,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const reqPath = asString(cfg.value.requirements);
|
|
174
|
+
if (reqPath === null || reqPath === '') {
|
|
175
|
+
ctx.refusals.push({
|
|
176
|
+
code: 'CG_GATE_UNDECLARED',
|
|
177
|
+
path: `${at} requirements`,
|
|
178
|
+
message: `${label}: ${configPath} names no requirements file; a CG config names one (config.requirements) declaring seam_over_motion.required: true and boundary_colour.max_step_excess, so the build runs both gates from the config alone`,
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const req = readObject(ctx.dir, resolve(dirname(cfg.file), reqPath), null);
|
|
183
|
+
if (!req.ok) {
|
|
184
|
+
ctx.refusals.push({ code: 'CG_GATES_UNREAD', path: `${at} requirements`, message: `${label}: the requirements file ${reqPath} (from ${configPath}): ${req.why}` });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const seam = isObject(req.value.seam_over_motion) ? req.value.seam_over_motion : {};
|
|
188
|
+
if (seam.required !== true) {
|
|
189
|
+
ctx.refusals.push({
|
|
190
|
+
code: 'CG_GATE_UNDECLARED',
|
|
191
|
+
path: `${at} requirements seam_over_motion.required`,
|
|
192
|
+
message: `${label}: ${reqPath} does not declare seam_over_motion.required: true; a CG build shows no background at any declared seam on any frame (parts CHECK_SEAM_OVER_MOTION_REQUIRED)`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const colour = isObject(req.value.boundary_colour) ? req.value.boundary_colour : {};
|
|
196
|
+
const allowance = colour.max_step_excess;
|
|
197
|
+
if (typeof allowance !== 'number' || !Number.isInteger(allowance) || allowance < 0) {
|
|
198
|
+
ctx.refusals.push({
|
|
199
|
+
code: 'CG_GATE_UNDECLARED',
|
|
200
|
+
path: `${at} requirements boundary_colour.max_step_excess`,
|
|
201
|
+
message: `${label}: ${reqPath} does not declare boundary_colour.max_step_excess as an integer; a CG build declares at most ${CG_BOUNDARY_ALLOWANCE} (parts CHECK_BOUNDARY_COLOUR_STEP)`,
|
|
202
|
+
});
|
|
203
|
+
} else if (allowance > CG_BOUNDARY_ALLOWANCE) {
|
|
204
|
+
ctx.refusals.push({
|
|
205
|
+
code: 'CG_BOUNDARY_ALLOWANCE_RAISED',
|
|
206
|
+
path: `${at} requirements boundary_colour.max_step_excess`,
|
|
207
|
+
message: `${label}: ${reqPath} declares boundary_colour.max_step_excess ${allowance}; a CG build declares at most ${CG_BOUNDARY_ALLOWANCE} — a step over it is the art's to fix (the piece's image or mesh), not the allowance's`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|