docs: align TI activate and status metadata with seeded-spec pattern
Amend ADR-0035 so Traffic Influence uses a platform-seeded service specification (app via application_reference), and rename qos_status metadata keys to capability_status across ADR-0036 and SRM/OEG contracts.
Changes for decisions/index.md: 2 added lines, 2 removed lines.
Original line number
Diff line number
Diff line
@@ -37,8 +37,8 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
*[ADR-0032](0032-databus-validation-failure-split-by-answerability.md) - DataBus validation failures split by answerability: dead-letter only unattributable messages; every readable-envelope failure returns failed_before_start
*[ADR-0033](0033-async-network-capability-caller-id.md) - async network-capability caller-facing ids (QoD sessionId, trafficInfluenceID) are OEG-minted and adopted as service_instance.id; external_ref reverts to the realization ref
*[ADR-0034](0034-network-capability-delete-is-async.md) - network-capability delete is async via command.srm.network.capability.deactivate, and its completion is consumed to a terminal record state
*[ADR-0035](0035-qod-session-service-specification.md) - a QoD session references one platform-seeded service specification held in OEG config; profile names stay unvalidated, and QoS profiles are a catalog-layer concern (resolves F2)
*[ADR-0036](0036-srm-owns-network-capability-expiry.md) - SRM owns expiry of async network capabilities and reports it via event.srm.operation.status metadata (qos_status_info); OEG runs no timer and maps it to CAMARA DURATION_EXPIRED
*[ADR-0035](0035-qod-session-service-specification.md) - a QoD session references one platform-seeded service specification held in OEG config; profile names stay unvalidated, and QoS profiles are a catalog-layer concern (resolves F2); amended 2026-10-20 so Traffic Influence uses the same seeded-spec pattern (app via `application_reference`)
*[ADR-0036](0036-srm-owns-network-capability-expiry.md) - SRM owns expiry of async network capabilities and reports it via event.srm.operation.status metadata (capability_status_info); OEG runs no timer and maps it to CAMARA DURATION_EXPIRED
*[ADR-0037](0037-canonical-target-ports.md) - target.device.ports / target.application_server.ports join srm.params/v1's canonical target descriptor, generic across every capability with a device/application-server target rather than allowlisted per capability_type
*[ADR-0038](0038-device-targeted-control-path-resolution.md) - device-targeted capabilities resolve their control path by probing candidate adapters with can_serve_target; the loop and the choice stay in SRM, the per-backend answer comes from TF-SDK
*[ADR-0039](0039-source-spec-stated-by-publisher.md) - source_spec is stated by the publishing component (OEG from CAMARA, FM from OPG.04) on network capability commands; SRM never derives it from the envelope source and never synthesizes a family, API, or version
Changes for oeg/interface-contract.md: 1 added line, 1 removed line.
Original line number
Diff line number
Diff line
@@ -323,7 +323,7 @@ This section describes the async messages OEG sends and receives.
| Subject | Action |
|---|---|
| `event.srm.operation.completed` | Read `instances[]`; update `operations` and the pre-created row(s) — `app_instances` (single for `/appinstances`, array for `/deployments`, ADR-0005) or `qod_sessions` / `traffic_influences` (single, ADR-0033). For a **deactivate** operation the target record is resolved from the operation's own `metadata`, and the record moves to a terminal state (ADR-0034). Trigger callback(s) |
| `event.srm.operation.status` | Update in-flight/backend-driven state; map backend capability state changes to CAMARA notifications. For async network capabilities, `metadata.qos_status` gives the new condition and `metadata.qos_status_info` the reason — `duration_expired` → CAMARA `DURATION_EXPIRED`, `network_terminated` (or absent) → `NETWORK_TERMINATED` (ADR-0036). Expiry is SRM-owned; OEG runs no timer. Events carrying no state this family recognizes are ignored without warning — the subject is shared platform-wide |
| `event.srm.operation.status` | Update in-flight/backend-driven state; map backend capability state changes to CAMARA notifications. For async network capabilities, `metadata.capability_status` gives the new condition and `metadata.capability_status_info` the reason — `duration_expired` → CAMARA `DURATION_EXPIRED`, `network_terminated` (or absent) → `NETWORK_TERMINATED` (ADR-0036). Expiry is SRM-owned; OEG runs no timer. Events carrying no state this family recognizes are ignored without warning — the subject is shared platform-wide |
| `event.srm.catalog.updated` | Invalidate `catalog_cache_entries` or Redis keys |
| `event.srm.inventory.drift_detected` / `event.srm.inventory.drift_resolved` | Log and emit metrics; optional Portal notification later |
Changes for oeg/persistence-model.md: 4 added lines, 4 removed lines.
Original line number
Diff line number
Diff line
@@ -72,10 +72,10 @@ A metering record for either synchronous or asynchronous API usage. Stored in `a
The retry and delivery history for outbound webhook notifications. Stored in `callback_deliveries`.
**QodSession**
The OEG-side record of a CAMARA QoD session (`POST /sessions`). `session_id` is **OEG-minted at request time and is the value SRM adopts as the session-holder `service_instance.id`** (ADR-0033) — the same mint-and-adopt relationship `AppInstance` has with `appInstanceId` (ADR-0005), so the CAMARA `sessionId`, OEG's primary key, and SRM's runtime root id are one value. The row is written eagerly (`state=REQUESTED`) alongside the `command.srm.network.capability.activate` publish (`capability_type=qod_session`). `state` transitions `REQUESTED` → `AVAILABLE`|`UNAVAILABLE` when that operation's `event.srm.operation.completed` is consumed, and again on backend-driven `event.srm.operation.status` (§L.2) — including **expiry**, which SRM owns and reports with `metadata.qos_status_info=duration_expired` (ADR-0036). OEG runs no timer over this table: `duration_seconds` is stored for the request record and for serving `expiresAt`, never to schedule work. `DELETE /sessions/{id}` publishes a second, distinct `command.srm.network.capability.deactivate` operation keyed on the same id, moves the row to `DELETION_REQUESTED`, and drives it to `DELETED`/`ERROR` when that operation completes (ADR-0034). On an `AVAILABLE` session, OEG also emits a `qos-status-changed` notification (`qosStatus=UNAVAILABLE`, `statusInfo=DELETE_REQUESTED`). Not a duplicate of SRM inventory: the row carries identity, ownership and cached status; capability detail (canonical parameters and result) is read live from `GET /internal/network-capabilities/{service_instance_id}`, the same live-read split `AppInstance` uses. Stored in `qod_sessions`.
The OEG-side record of a CAMARA QoD session (`POST /sessions`). `session_id` is **OEG-minted at request time and is the value SRM adopts as the session-holder `service_instance.id`** (ADR-0033) — the same mint-and-adopt relationship `AppInstance` has with `appInstanceId` (ADR-0005), so the CAMARA `sessionId`, OEG's primary key, and SRM's runtime root id are one value. The row is written eagerly (`state=REQUESTED`) alongside the `command.srm.network.capability.activate` publish (`capability_type=qod_session`). `state` transitions `REQUESTED` → `AVAILABLE`|`UNAVAILABLE` when that operation's `event.srm.operation.completed` is consumed, and again on backend-driven `event.srm.operation.status` (§L.2) — including **expiry**, which SRM owns and reports with `metadata.capability_status_info=duration_expired` (ADR-0036). OEG runs no timer over this table: `duration_seconds` is stored for the request record and for serving `expiresAt`, never to schedule work. `DELETE /sessions/{id}` publishes a second, distinct `command.srm.network.capability.deactivate` operation keyed on the same id, moves the row to `DELETION_REQUESTED`, and drives it to `DELETED`/`ERROR` when that operation completes (ADR-0034). On an `AVAILABLE` session, OEG also emits a `qos-status-changed` notification (`qosStatus=UNAVAILABLE`, `statusInfo=DELETE_REQUESTED`). Not a duplicate of SRM inventory: the row carries identity, ownership and cached status; capability detail (canonical parameters and result) is read live from `GET /internal/network-capabilities/{service_instance_id}`, the same live-read split `AppInstance` uses. Stored in `qod_sessions`.
**TrafficInfluence**
The OEG-side record of a CAMARA Traffic Influence policy (`POST /traffic-influences`). Structurally identical to `QodSession`: `traffic_influence_id` is OEG-minted, adopted by SRM as `service_instance.id` (ADR-0033), written eagerly (`state=ordered`) alongside the activate publish (`capability_type=traffic_influence`), advanced by the completion and status events, and torn down by a second deactivate operation (ADR-0034). It differs in one respect — its `service_specification_id` is **resolved, not minted**: a Traffic Influence request always targets an already-registered `appId`, which OEG resolves to the owning `app_registration_id`(= `service_specification_id`, ADR-0011 — note `app_id` itself is **not** the specificationid). Stored in `traffic_influences`.
The OEG-side record of a CAMARA Traffic Influence policy (`POST /traffic-influences`). Structurally identical to `QodSession`: `traffic_influence_id` is OEG-minted, adopted by SRM as `service_instance.id` (ADR-0033), written eagerly (`state=ordered`) alongside the activate publish (`capability_type=traffic_influence`), advanced by the completion and status events, and torn down by a second deactivate operation (ADR-0034). The activate command's `service_specification_id` is the **platform-seeded** Traffic Influence specification held in OEG config (ADR-0035 amendment), the same pattern as QoD. The request's required `appId` is still resolved to `app_registration_id`for this row's bookkeeping and for notification scoping, and is forwarded to SRM as `application_reference` so SRM can pick the workload instance — it is **not** the command's `service_specification_id`. Stored in `traffic_influences`.
**CatalogCacheEntry**
An optional cached fragment of catalog data with TTL or version information. Lives in Redis/in-process TTL cache **only** — never a PostgreSQL table (persistent storage risks stale reads; see "What is intentionally not persisted" below).
> Who may call QoD is the `quality-on-demand` product grant (ADR-0048). Neither step changes the
> command shape, this table, or the CAMARA surface.
**`traffic_influences`** — the OEG-side record for `POST /traffic-influences`. Same shape and lifecycle as `qod_sessions`: `traffic_influence_id` is OEG-minted and adopted by SRM as `service_instance.id` (ADR-0033), the row is written at request time, advanced by the activate completion, and driven to a terminal state by the deactivate completion (ADR-0034). It needs no seeded specification (ADR-0035): its `service_specification_id` is resolved from the request's required `appId` via `app_registrations.app_registration_id` (ADR-0011).
**`traffic_influences`** — the OEG-side record for `POST /traffic-influences`. Same shape and lifecycle as `qod_sessions`: `traffic_influence_id` is OEG-minted and adopted by SRM as `service_instance.id` (ADR-0033), the row is written at request time, advanced by the activate completion, and driven to a terminal state by the deactivate completion (ADR-0034). The activate command uses the platform-seeded TI specification in OEG config (ADR-0035 amendment); `app_registration_id` on this row is resolved from `appId` for ownership and notification scoping only.
| `operation_id` | UUID FK -> `operations` | The **activate** operation that created this row |
| `app_registration_id` | UUID FK -> `app_registrations` | Resolved from the request's CAMARA `appId`; supplies `service_specification_id`on the command (ADR-0011) |
| `app_registration_id` | UUID FK -> `app_registrations` | Resolved from the request's CAMARA `appId` (ADR-0011); OEG bookkeeping / `selected_appInstanceId` scoping — **not** the activate command's `service_specification_id` |
| `edge_cloud_zone_id` | UUID | NULLABLE; the zone OEG pins on the activate command (`zone_id`): the requested CAMARA `edgeCloudZoneId`, else the zone resolved from `edgeCloudRegion` or `appInstanceId` (an `edgeCloudRegion` with no zones, or an `appInstanceId` SRM cannot resolve, is rejected `404`, never sent unpinned). SRM realizes the policy only inside a pinned zone (ADR-0045), so this is also the policy's zone; it scopes the `ready``app_instances` reported as `selected_appInstanceId` on the `active` notification (only the requested one when `appInstanceId` was given). NULL when the request names no location — SRM then chooses the control path and no instance is reported as selected |
| `edge_cloud_region` | VARCHAR(256) | NULLABLE; the requested CAMARA `edgeCloudRegion`, resolved to a zone before the command is built |
| `source_port` | INTEGER | NULLABLE; from `sourceTrafficFilters.sourcePort` |