# DEVX-159: Fix build_image push-first strategy to avoid losing latest tag ## Problem The `push_image` function in `build_image.py` deletes the existing manifest *before* pushing (Gitea #31964 workaround). When the push fails for other reasons (HTTP 500), the old tag is lost, breaking all CI jobs that use that image. This caused `ci-base:latest` to disappear from the registry when build-images run #4104 failed with HTTP 500 on push, after already deleting the old `latest` manifest. ## Approach Switch to a push-first strategy: 1. Try pushing directly 2. Only if push fails with "already exists" (Gitea #31964), delete the old manifest and retry 3. If push fails for any other reason, the old manifest is preserved REQ-1: Push first, no pre-emptive delete REQ-2: Delete + retry only on "already exists" error REQ-3: Old manifest preserved on non-already-exists failures REQ-4: 100% test coverage of new logic ## Test Plan - Unit tests for all push paths (success, already-exists retry, non-already-exists failure, retry-also-fails) - Verify existing tests still pass ## Deploy Plan - Merge to master, build-images workflow uses new push logic on next image rebuild ## Rollback Plan - Revert the merge commit ## Acceptance Criteria - [x] REQ-1: Push first, no pre-emptive delete - [x] REQ-2: Delete + retry only on "already exists" error - [x] REQ-3: Old manifest preserved on non-already-exists failures - [x] REQ-4: 100% test coverage of new logic