ci/woodpecker/pr/pr-review Pipeline was successful
The ci-claude image installed a fixed, pinned Claude Code version and every consumer pipeline pinned a stale dated image tag. Two problems: - The npm install layer changed on every build (install-time mtimes + the npm cache), so it never deduped in the Gitea registry — the single largest orphan pattern behind #336 (~260 MB orphaned per no-op rebuild). - A frozen version silently rots: old Claude Code releases eventually stop working against the API. Containerfile: default to @latest (npm auto-selects the musl native binary on this alpine base) and make the install layer byte-stable — clean the npm cache and normalize install-time mtimes on the installed tree and the directories the RUN touches. Two independent --no-cache builds now produce a byte-identical npm layer, so a rebuild of the same version dedups instead of orphaning a copy. New ci-claude-refresh.yml owns the image lifecycle: weekly (and on-demand) it resolves the latest published version, and only when it differs from what the consumers pin does it build ci-claude:<version> (+:latest) and open a PR rewiring the 5 consumer pipelines. Version tags make "did it change?" a race-free string compare and keep the cron from opening no-op PRs. Nothing auto-merges. build-ci-images.yml no longer builds ci-claude so the two never fight over tags. Requires owner setup before it does anything (documented in the pipeline header): a dedicated ci-claude-bump-bot identity (write:repository only) as the ci_claude_bump_bot_token secret, and a cron named "ci-claude-refresh". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>