fix(eam): show refused attempts and hide confirmed deletions

Instance list, refused partner deployment:

sequenceDiagram
  participant AP as App Provider
  participant OEG
  participant SRM
  participant FM
  AP->>OEG: POST /appinstances (Paris zone)
  OEG-->>AP: 202
  OEG->>SRM: command.srm.service.deploy
  SRM->>FM: command.fm.service.deploy
  FM-->>SRM: partner refused
  SRM-->>OEG: event.srm.operation.completed (failed + error)
  OEG->>OEG: save failed attempt, release zone
  AP->>OEG: GET /appinstances
  OEG->>SRM: GET /internal/service-instances
  SRM-->>OEG: nothing for the refused attempt
  Note over OEG: before: attempt missing from the list<br/>now: listed as failed for its App Provider
  OEG-->>AP: SRM rows + failed attempt, minus confirmed deletions

App list (second commit):

flowchart LR
  G["GET /apps"] -- "GET /internal/catalog/service-specifications" --> S["SRM specs<br/>(retired specs still listed)"] --> Q{"OEG registration<br/>DELETED?"}
  Q -- "yes" --> X["left out<br/>(same as GET /apps/{appId}, 404)"]
  Q -- "no, or never registered here" --> L["listed"]
flowchart LR
  I["fix/instance-list-lifecycle<br/>OEG !43 (this MR)"] -- "targets now" --> A["feat/agreements-relay<br/>OEG !42"] -- "merges first" --> D["develop"]
  I -. "retarget after !42 merges" .-> D

A partner deployment refused before fulfillment has no runtime instance for SRM to return. Even after OEG consumes the failure, GET /appinstances omitted the accepted attempt. A confirmed delete could also reappear as unknown from an old runtime transaction.

Supplement SRM results with OEG's saved failed attempts, scoped by the original operation's App Provider. Apply app, instance and region filters, retain the requested name, and return SRM's runtime result once when both sources have an instance. Suppress a runtime row when OEG has confirmed that instance as terminated. Historical rows that missed earlier terminal events are not rewritten.

A second commit fixes GET /apps listing an app after DELETE /apps/{appId} answered 204. SRM only retires the specification of an app that ever had instances, and its list still returns it. The list now skips specifications whose registration here is marked deleted, which matches GET /apps/{appId} (404 for the same app). Specifications OEG never registered are listed as before.

The accompanying architecture amendment explicitly changes the previous SRM-only read contract. Please review that decision with this MR. SRM remains the runtime source, and failed attempts receive no invented endpoints or cluster references.

Validation: 1234 unit tests passed, 1 skipped (2 new for the app list); 40 integration tests passed, including provider isolation against Postgres; mypy, ruff and import-linter passed. The portal UI showed two refused attempts as failed, accepted the retry with 202, and removed a successfully deleted Paris instance.

Target feat/agreements-relay to exclude its parent commit from the initial diff; retarget to develop after that merges. The stale-409 fix also requires SRM to emit valid failed completion entries with errors.

Edited by Sergio Gimenez

Merge request reports

Loading
Loading