feat(ci): write-field-key.sh store-mode via CRAIG_CI_AGE_KEY (U7)
Summary
Switch devstack/ci/write-field-key.sh to store-mode: CRAIG_CI_AGE_KEY unset => hard
error naming the variable + cargo xtask secrets init --for-ci (ADR-048 §D5 fail-loud
preserved); decrypt ONLY inside the pinned-digest container (the key never reaches
unrelated tooling; unset after the step); output validated (single line, base64,
32 bytes), atomic no-symlink-follow write, prior file retained on failure; test seams
for BOTH input (CRAIG_SECRETS_FILE) and output path so local docker:27 proofs can
never clobber a real dev key. Scheduled scan jobs constrained to protected refs. The old
CRAIG_FIELD_ENCRYPTION_KEY path is REPLACED (no dual-path window); the old variable
stays inert until gate G-C retires it after one observed real scan-job success.
Acceptance criteria
- BLOCKED until #1031 (closed) G-B checked (policy job green with the real CI key).
- Local docker:27 proof: correct value written 0600; invalid/empty/wrong-length
extraction fails with the prior file retained; unset-variable error text names
CRAIG_CI_AGE_KEY. - ADR-048 §D5 amendment + CHANGELOG pre-1.0 Changed entry (variable replacement;
forks run
secrets fork-rebootstrap). - G-C checkbox activated on #1031 (closed) (observe one real scan-job success, then delete the old variable).
Plan: repo-committed-secrets.adoc (U0) — the plan page and ADR-064 carry the full design; this issue is self-contained for its unit.
Parent program: #1032 (closed) (epic &79 (closed)). External v1 review 2026-08-10 (~40 findings) reshaped the architecture; the plan's Architecture section is the reviewer's 9-point direction.