AST-4 promote negative-control receipt Run: ast-4-builder-promote Checkout: Base: fefe710b5b7aa8e0bc1c4d09d7ef5c4f81c8e11b Red first Command: npx vitest run tests/promote.test.ts Observed: test suite failed because src/store/promote.js did not exist. CLI wiring control Command: npx vitest run tests/promote.test.ts Observed before CLI wiring: 8 passed, CLI test failed; expected exit 0, observed exit 2. Malformed --to exit guard Mutation: promote-target used cannotSupply instead of refused. Command: npx vitest run tests/promote.test.ts -t 'malformed --to' Observed: failed; expected exit 2, observed exit 4. Mutation restored. Absent dev pointer exit guard Mutation: promote-dev-pointer used cannotSupply instead of refused. Command: npx vitest run tests/promote.test.ts -t 'absent dev pointer' Observed: failed; expected exit 2, observed exit 4. Mutation restored. Missing --to classification guard Mutation: disabled explicit missing-manifest classification. Command: npx vitest run tests/promote.test.ts -t 'absent --to manifest' Observed: failed; expected promote-target-missing, observed promote-incomplete. Mutation restored. Incomplete closure guard Mutation: disabled checked.missing refusal. Command: npx vitest run tests/promote.test.ts -t 'incomplete closure' Observed: failed; expected Problem, observed null. Mutation restored. Corrupt store guard Mutation: disabled checked.mismatched/checked.invalid refusal. Command: npx vitest run tests/promote.test.ts -t 'corrupt store bytes' Observed: failed; expected Problem, observed null. Mutation restored. Pointer order guard Mutation: wrote env/prod/manifest.json before the pack pointer. Command: npx vitest run tests/promote.test.ts -t 'writes prod pack pointers' Observed: failed; expected pack pointer then manifest pointer, observed manifest pointer then pack pointer. Mutation restored. Restored focused gate Command: npx vitest run tests/promote.test.ts tests/verify.test.ts tests/cli.test.ts Observed: 3 files passed, 27 tests passed. Full gate Command: just verify Observed: format/lint/types/dependencies/ratchets/docs green; 9 test files passed, 1 credential-gated R2 file skipped; 68 tests passed, 1 skipped. R2 was not run: promote calls writePackPointer/writePointer, both using the same unconditional ObjectStore.putPointer primitive already exercised by publish's R2 integration test. Filesystem controls exercise both prod pointer writes and assert their order and that immutable bytes are unchanged. Verify extraction compatibility control Initial comparison: the extracted implementation changed the position of the pack-pointer log and report entry; the base-versus-branch comparison failed. The hook was moved into the manifest walk and the mutation was thereby restored. Command: run the same fixed-time synthetic publish and verify against base fefe710 and the branch, then compare the serialized log lines and report byte-for-byte. Observed: identical SHA-256 e17f1c665aec849adb75afb8c624813c6bab4ca484cb34150988927872f87b18; 10 log lines, 10 present, 0 missing, 0 mismatched, 0 invalid, 0 drift. Final size Command: git diff --numstat plus line counts for the two untracked files. Observed: 499 changed lines (227 tracked additions/deletions plus 272 new-file lines).