diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md
index b0581b02d..c87cb521a 100644
--- a/docs/OPERATIONS.md
+++ b/docs/OPERATIONS.md
@@ -18,7 +18,7 @@
Docker Verify builds each service locally (`load: true`, no push) and runs pinned Trivy v0.74.0 against that local image. Docker Publish pushes digest-only candidates: it does not assign normal `sha-*` or `v*` release tags while individual service gates are running.
-Each service must pass HIGH/CRITICAL Trivy scanning, then be signed and receive SPDX/SLSA attestations for the same candidate digest. The `promote` job runs only after all nine matrix builds succeed; it validates all nine reports, source commit, digest subjects, signer identity, and verified DSSE predicates before inspecting or writing any tags.
+Each service must pass HIGH/CRITICAL Trivy scanning, then be signed and receive SPDX/SLSA attestations for the same candidate digest. The `promote` job runs only after all nine matrix builds succeed; it validates all nine reports, source commit, digest subjects, signer identity, and verified DSSE predicates before inspecting or writing any tags. A changed backend image requires a new service release version in both `services/pom.xml` and its owner POM; an existing version tag may only be reused for the identical digest. Keep contract fixtures aligned with the intended release versions instead of weakening the immutable-tag guard.
Use `release-manifest` artifact from successful Docker Publish run as canonical deployment input. It contains complete nine-service `release-manifest.txt`, `release-set.json`, and per-service evidence. Local `scripts/runbooks/promote-release.sh EVIDENCE_DIR SOURCE_SHA ghcr.io/davidhlp/ulticode` supports read-only dry-run only; `--execute` is restricted to Docker Publish on `main`. The workflow uses repository-wide `ulticode-release-tag-writer` concurrency with cancellation disabled. If promotion fails after candidate builds succeed, rerun only the failed `promote` job in that same Actions run, while its nine candidate artifacts remain within their retention period; do not rerun successful `build-and-push` jobs, since a rebuild may produce a different digest and collide with immutable tags. The workflow is push-only and has no `workflow_dispatch`. Do not promote tags through local commands or other writers. This serializes repository-controlled writers, not external registry actors, and GHCR offers no compare-and-swap guarantee here. Existing manifest conflicts fail before registry inspection/writes; all planned tags are read back after promotion, including tags that already existed, before complete manifests are written. Matching existing tags are idempotent; a different digest is a hard failure. A mid-promotion failure may leave already verified tags in place, but emits no complete manifest. Never retarget or delete old tags/digests; retain previous successful release artifact for rollback.
diff --git a/scripts/test/promote-release-contract.py b/scripts/test/promote-release-contract.py
index b6d7a5452..0aba7f243 100755
--- a/scripts/test/promote-release-contract.py
+++ b/scripts/test/promote-release-contract.py
@@ -43,7 +43,7 @@ def seed(base):
(folder / f"{name}.spdx.json").write_text(json.dumps(spdx))
(folder / f"{name}.provenance.json").write_text(json.dumps(provenance))
(folder / f"{name}.trivy.json").write_text(json.dumps(trivy))
- (folder / f"{name}.version").write_text("1.0.1\n" if service.get("maven_version_property") else "\n")
+ (folder / f"{name}.version").write_text("1.0.2\n" if service.get("maven_version_property") else "\n")
(folder / f"{name}.source-commit").write_text(SHA + "\n")
return root
@@ -163,7 +163,7 @@ def main():
image = f"{PREFIX}/{service_name}"
existing_tags[f"{image}:sha-{SHA[:7]}"] = DIGEST
if service.get("maven_version_property"):
- existing_tags[f"{image}:v1.0.1"] = DIGEST
+ existing_tags[f"{image}:v1.0.2"] = DIGEST
state.write_text(json.dumps(existing_tags)); log.write_text("")
key = f"{PREFIX}/{MATRIX[0]['name']}:sha-{SHA[:7]}"
result = call(root, bindir, state, log, "--execute", **GITHUB_WRITER, BAD_FINAL_READBACK=key)
diff --git a/scripts/test/supply-chain-contract.sh b/scripts/test/supply-chain-contract.sh
index b6c084a2b..1aee36035 100755
--- a/scripts/test/supply-chain-contract.sh
+++ b/scripts/test/supply-chain-contract.sh
@@ -148,7 +148,7 @@ for service in owner_services:
owner_pom = repo / 'services' / service['module'].split('/', 1)[0] / 'pom.xml'
owner_properties = ET.parse(owner_pom).getroot().find('m:properties', ns)
owner_version = owner_properties.findtext(f'm:{prop}', namespaces=ns)
- assert root_version == owner_version == '1.0.1', f'{service["name"]}: release version mismatch ({root_version!r} != {owner_version!r})'
+ assert root_version == owner_version == '1.0.2', f'{service["name"]}: release version mismatch ({root_version!r} != {owner_version!r})'
assert 'actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333' in workflow
print('digest-only candidates, common normalized repo and all-service promote gate: PASS')
scan = workflow.split(' - name: Scan pushed candidate\n', 1)[1].split(' - name:', 1)[0]
diff --git a/services/admin/pom.xml b/services/admin/pom.xml
index 5a0c7e441..b789f733e 100644
--- a/services/admin/pom.xml
+++ b/services/admin/pom.xml
@@ -13,7 +13,7 @@
- 1.0.1
+ 1.0.2
backend-admin
${service.version.admin}
diff --git a/services/app/pom.xml b/services/app/pom.xml
index cc3bef1a0..eb0395426 100644
--- a/services/app/pom.xml
+++ b/services/app/pom.xml
@@ -13,7 +13,7 @@
- 1.0.1
+ 1.0.2
backend-app
${service.version.app}
diff --git a/services/auth/pom.xml b/services/auth/pom.xml
index 31c16dbce..8d687835d 100644
--- a/services/auth/pom.xml
+++ b/services/auth/pom.xml
@@ -13,7 +13,7 @@
- 1.0.1
+ 1.0.2
backend-auth
${service.version.auth}
diff --git a/services/judge/pom.xml b/services/judge/pom.xml
index 801378ed1..b7e937c01 100644
--- a/services/judge/pom.xml
+++ b/services/judge/pom.xml
@@ -15,7 +15,7 @@
- 1.0.1
+ 1.0.2
backend-judge
diff --git a/services/notification/pom.xml b/services/notification/pom.xml
index c7e684efd..5eb6ecc0a 100644
--- a/services/notification/pom.xml
+++ b/services/notification/pom.xml
@@ -14,7 +14,7 @@
- 1.0.1
+ 1.0.2
backend-notification
${service.version.notification}
diff --git a/services/pom.xml b/services/pom.xml
index 7538cfb18..98e9a3e04 100644
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -52,13 +52,13 @@
- 1.0.1
- 1.0.1
- 1.0.1
- 1.0.1
- 1.0.1
- 1.0.1
- 1.0.1
+ 1.0.2
+ 1.0.2
+ 1.0.2
+ 1.0.2
+ 1.0.2
+ 1.0.2
+ 1.0.2
17
2.21.7
diff --git a/services/search/pom.xml b/services/search/pom.xml
index 1cbc6e90a..514bb3b5a 100644
--- a/services/search/pom.xml
+++ b/services/search/pom.xml
@@ -14,7 +14,7 @@
- 1.0.1
+ 1.0.2
backend-search
diff --git a/services/submission/pom.xml b/services/submission/pom.xml
index 817a8ba36..c8840d258 100644
--- a/services/submission/pom.xml
+++ b/services/submission/pom.xml
@@ -15,7 +15,7 @@
- 1.0.1
+ 1.0.2
backend-submission