fix(federation): complete partner instance lifecycle correctly
Partner instance delete:
sequenceDiagram
participant OEG
participant SRM
participant DB as SRM Postgres
participant FM
OEG->>SRM: command.srm.service.terminate
SRM->>FM: GET /internal/federated-service-instances/{id}
FM-->>SRM: instance + zone
SRM->>DB: service_order, target_service_instance_id NULL<br/>instance + zone in the order hand-off
Note over SRM,DB: before: FK to a service_instance row SRM never creates, insert failed
SRM->>FM: command.fm.service.terminate
FM-->>SRM: event.fm.operation.completed
SRM-->>OEG: event.srm.operation.completedRefused partner deploy:
sequenceDiagram
participant OEG
participant SRM
participant FM
SRM->>FM: command.fm.service.deploy
FM-->>SRM: event.fm.operation.completed (failed, error)
SRM-->>OEG: event.srm.operation.completed<br/>failed entry WITH error (FM's or a generic problem)
Note over OEG: before: entry had no error, OEG rejected the event, zone stayed busy<br/>now: app/zone pair free for an immediate retryflowchart LR
H["fix/partner-instance-lifecycle<br/>SRM !37 (this MR)"] --> W["refactor/tf-sdk-wiring"] -- "SRM !33" --> P["refactor/tf-sdk-ports"] -- "SRM !16" --> D["develop"]Deleting a partner-hosted instance failed before RemoveApp because SRM wrote a service_order foreign key pointing to a service_instance row it intentionally never creates. A refused partner deployment also produced a failed completion entry without the required error, causing OEG to reject the event and retain a stale busy-zone row.
Leave target_service_instance_id null for the partner terminate and retain its instance/zone in the order-local hand-off. Read FM's internal instance endpoint to obtain the zone required by the completion contract; an instance unknown to both components fails before starting. Preserve FM's error on failed targets, supply a generic problem when absent, and include each failed instance's error on both normal and replay paths, including partially completed orders.
Validation: 486 tests passed with mypy, ruff and import-linter clean in the prior session. The real-Postgres terminate regression fails without the FK fix. On the VM stack, partner deletion reached FM and removed the workload; refused attempts released the app/zone pair for an immediate retry.
Target Paris's refactor/tf-sdk-wiring branch so the MR contains only the hotfix. It then travels through SRM !33 (merged) into refactor/tf-sdk-ports, and !16 (merged) into develop. Needs FM !34 (merged)'s instance-read route and the ADR-0053/SRM contract amendment. SDK naming is a separate package/dependency release step. Pipeline 28572 fails test with 6 failures. The base branch fails the same 6 (pipeline 28530): test_topology_bootstrap.py and test_tfsdk_registry_wiring.py read ../tf-sdk/tests/contract/bootstrap_example.yaml, which exists only when tf-sdk is checked out next to SRM, and CI doesn't do that. This MR: 480 passed, 6 failed. Base: 477 passed, 6 failed. format, lint, type and architecture pass. The fixture fix belongs on refactor/tf-sdk-wiring (copy the YAML into SRM's tests, or skip when it's absent).