Commit b457f05b authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

docs(oeg): carry the TI seeded-spec decision into the restructured spec

Port 9294ce58 onto the restructured OEG docs: contract §2 and §9 use the
renamed capability_status keys, and the TrafficInfluence entity and
traffic_influences table take the platform-seeded specification, with
appId as application_reference.

C02 is now decided by the ADR-0035 amendment. Design §K.2 and §N.3,
delivery §P/§T/§U and contract §9/§11 say so, and keep the remaining gap:
the referenced SRM revision predates the traffic-influence seed. Note in
ADR-0033 that its TI remark is superseded, and drop a stray character in
srm/architecture.md.

Co-Authored-By: default avatarClaude Opus 5.5 <noreply@anthropic.com>
parent a19a604a
Loading
Loading
Loading
Loading
+2 −1
Changes for decisions/0033-async-network-capability-caller-id.md: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -126,7 +126,8 @@ Deferred or excluded:
- The QoD `service_specification_id` question is out of scope here and settled separately by
  ADR-0035: the platform seeds one QoD specification at deployment and OEG reads its id from
  configuration. Traffic Influence needs no seeded row — it resolves its specification from its
  required `appId` (`app_registration_id`, ADR-0011).
  required `appId` (`app_registration_id`, ADR-0011). Superseded for Traffic Influence by the
  ADR-0035 amendment of 2026-10-20: TI also uses a platform-seeded specification.

Revisit trigger:

+3 −3
Changes for oeg/implementation-and-delivery.md: 3 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ The implementation uses native auth, logging, health and core NATS adapters. Sha
| Callback | `CALLBACK_SETTINGS__TIMEOUT`, `__SECRET_KEY` | Default 10 s and unset; key must decode from base64url to exactly 32 bytes, validated at startup |
| QoD seed | `QOD_SETTINGS__SERVICE_SPECIFICATION_ID` | UUID default derived from `qod-session`; must match SRM seed |
| Location seed | `LOCATION_RETRIEVAL_SETTINGS__SERVICE_SPECIFICATION_ID` | UUID default derived from `location-context`; must match SRM seed |
| TI seed | `TI_SETTINGS__SERVICE_SPECIFICATION_ID` | UUID default derived from `traffic-influence`; selected SRM does **not** seed it (C02) |
| TI seed | `TI_SETTINGS__SERVICE_SPECIFICATION_ID` | UUID default derived from `traffic-influence`; must match SRM seed, which the referenced SRM revision predates (C02) |
| FM | `FM_SETTINGS__BASE_URL`, `__TIMEOUT` | HTTP URL default `http://localhost:8083`, 30 s; internal listener, not EWBI 8082 |
| Reconciliation | `RECONCILIATION_SETTINGS__ENABLED`, `__INTERVAL_SECONDS`, `__OPERATION_DEADLINE_SECONDS` | Defaults true, 60 s, 1800 s; force-fails stranded EAM operations, not backend cancellation |

@@ -156,7 +156,7 @@ Unknown acceptance is written explicitly; code absence is not a justification fo

| Gap | Why It Is Acceptable for Now | Workaround |
|---|---|---|
| C01/C02 QoD expiry owner and incompatible TI specification seed | Acceptance and accountable owner Unspecified | No agreed workaround; align decisions, OEG and SRM before relying on lifecycle correctness |
| C01 QoD expiry owner; C02 TI seed absent from the referenced SRM revision | C01 acceptance and accountable owner Unspecified; C02 is decided (ADR-0035 amendment) | No agreed C01 workaround; for C02, run against an SRM revision that seeds `traffic-influence` |
| C03/C04/C21 required outbox, broker-acknowledged EAM 202, changed-request 409 and replay/concurrency | These requirements do not establish acceptance of the implementation gap; acceptance/owner Unspecified | No guaranteed workaround; selected code lacks the outbox and required replay rule. Failure/retry/equivalence and operator policies remain Unspecified. |
| C09 private registry secret store/resolver | Acceptance/owner Unspecified | Reference generation alone is insufficient; no established workaround |
| C10 callbacks have one attempt; OQ-03 retry missing | Retry remains required; acceptance/owner Unspecified | Caller resource polling observes status; it does not fulfil notification delivery |
@@ -181,7 +181,7 @@ No delivery schedule is established for the work below. Required behaviour remai
| Item | Missing | Plan | Already in Place |
|---|---|---|---|
| Reliable async lifecycle | Required outbox, broker-acknowledged EAM 202, changed-request 409, durable delivery and replay/late-event policy | Implement required outbox, acknowledgment and 409 behaviour; detailed recovery/equivalence and release remain Unspecified | SQL operations, event handlers and forced-failure sweep; no outbox or durable broker acknowledgment |
| Network alignment | QoD expiry/lifetime authority and TI specification agreement | Resolve C01/C02/C07/C08; implementation/release Unspecified | QoD/TI holders, activate/deactivate paths, QoD/location seeds in SRM |
| Network alignment | QoD expiry/lifetime authority; an SRM revision with the TI seed | Resolve C01/C07/C08 and move the SRM reference for C02; implementation/release Unspecified | QoD/TI holders, activate/deactivate paths, QoD/location seeds in SRM |
| Policy and discovery | Quota, usage/IEC/PEC, TMF facade and cache invalidation | Rate limit deferred; other sequence/release Unspecified | Roles/scopes, provider checks, EAM catalog reads |
| Notifications and operations | Retry/dead-letter, callback endpoint detail decision, operation GET | OQ-03/OQ-04/C11 decisions first; schedule Unspecified | Encrypted/tombstoned sink tokens and single-attempt delivery |
| API/operations completeness | Deployment PATCH policy, subscriber consent/routing, retention/migrations/backup, metrics | Proposals and decisions only; no invented delivery commitment | Existing supported family routes and native probes/logs |
+7 −7
Changes for oeg/interface-contract.md: 7 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -442,7 +442,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). Intended ADR-0036 expiry is SRM-owned with no OEG timer; selected OEG contradicts this (C01). 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). Intended ADR-0036 expiry is SRM-owned with no OEG timer; selected OEG contradicts this (C01). 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 |

@@ -2288,7 +2288,7 @@ Why: [ADR-0006](/decisions/0006-oeg-db-schema-consolidation.md), [ADR-0010](/dec

**Intended dictionary, not an SQL introspection dump.** Types/indexes and conceptual relationships are preserved below. Selected SQL implements nine tables; `api_subscriptions` and `api_usage_records` are absent. Active app-id uniqueness is partial, allowing soft-deleted ids to be reused locally. Extra observed registration/provider and QoD target/timing fields are listed after the intended tables.

**Disputed rules:** ADR-0036 assigns QoD expiry to SRM with no OEG timer, but selected OEG expires sessions itself. TI's intended application-specification reference conflicts with its configured seed. Local QoD detail exceeds the thin/live-read intent. QoD `webhook.notificationUrl` / `webhook.notificationAuthToken` / `org.camaraproject.qod.v0.qos-status-changed` belong to upstream v0.10.1; selected v1.1 uses sink fields and a different event type. Start-based credential lifetime remains disputed against the observed acceptance-based cap. C01/C02/C07/C08 identify the required resolutions.
**Disputed rules:** ADR-0036 assigns QoD expiry to SRM with no OEG timer, but selected OEG expires sessions itself. TI's seeded specification is decided (ADR-0035 amendment), but the referenced SRM revision predates that seed. Local QoD detail exceeds the thin/live-read intent. QoD `webhook.notificationUrl` / `webhook.notificationAuthToken` / `org.camaraproject.qod.v0.qos-status-changed` belong to upstream v0.10.1; selected v1.1 uses sink fields and a different event type. Start-based credential lifetime remains disputed against the observed acceptance-based cap. C01/C02/C07/C08 identify the required resolutions.

Provider checks apply to reads/deletes; organization sharing remains F5. JWT/JWKS handling is native. Intermediate event history is not persisted; EAM early status updates IN_PROGRESS.

@@ -2323,10 +2323,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` (§2) — including **expiry**, which ADR-0036 assigns to SRM and reports with `metadata.qos_status_info=duration_expired` (ADR-0036). **Intended ADR-0036 rule, contradicted by selected OEG (C01):** 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` (§2) — including **expiry**, which ADR-0036 assigns to SRM and reports with `metadata.capability_status_info=duration_expired` (ADR-0036). **Intended ADR-0036 rule, contradicted by selected OEG (C01):** 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 specification id). 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).
@@ -2587,13 +2587,13 @@ Indexes: `BTREE(operation_id)`, `BTREE(tenant_id)`.
> 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.

| Column | Type | Notes |
|---|---|---|
| `traffic_influence_id` | UUID PK | OEG-minted; the CAMARA `trafficInfluenceID` **and** SRM's `service_instance.id` |
| `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` |
@@ -2777,7 +2777,7 @@ Content-Type: application/json
}
```

The 201 resource adds minted `trafficInfluenceID` and `state=ordered`; activation later makes it active/error. The activate command carries traffic filters, application reference, selected zone and source provenance (`traffic-influence`, `0.10.0`). Intended specification resolution uses the app registration; observed publishing instead uses configured TI specification id, absent from selected SRM seed bootstrap. This example validates request shape, not end-to-end TI fulfilment.
The 201 resource adds minted `trafficInfluenceID` and `state=ordered`; activation later makes it active/error. The activate command carries traffic filters, application reference, selected zone and source provenance (`traffic-influence`, `0.10.0`). Its `service_specification_id` is the platform-seeded TI specification from OEG configuration, and `appId` travels as `application_reference` (ADR-0035 amendment); the referenced SRM revision predates that seed (C02). This example validates request shape, not end-to-end TI fulfilment.

DELETE accepts asynchronously with 202, moving through `deletion in progress` to `deleted`. Individual deleted reads remain 200; collection excludes them. Selected update/devices routes return 501. A ready instance reported in `selected_appInstanceId` must belong to the pinned zone, and only the requested instance may be selected when supplied.

+2 −2
Changes for oeg/requirements-and-design.md: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -176,7 +176,7 @@ Why: [ADR-0033](/decisions/0033-async-network-capability-caller-id.md), [ADR-003
4. Completion/status moves to `active`/`error`. Pinned-zone ready instances scope `selected_appInstanceId`; the reviewed implementation returns 501 for device/update routes.
5. DELETE publishes a distinct deactivate operation, returns 202 and advances `deletion in progress` → `deleted`. A deleted individual resource remains representable with 200; it is excluded from active collection results.

**Blocker C02:** intended specification id is the app registration, but selected OEG sends a configured TI seed absent from selected SRM bootstrap. No compatible workaround or acceptance is established. [Contract §11](interface-contract.md#11-network-api-contracts) retains field rules and examples; deciding the specification model precedes reliable integration.
**C02 (decided, not yet integrated):** TI uses a platform-seeded specification from OEG configuration, as QoD does, and sends `appId` as `application_reference` for SRM to resolve the workload ([ADR-0035](/decisions/0035-qod-session-service-specification.md) amendment). The app registration serves OEG ownership and notification scoping only. The referenced SRM revision predates the TI seed, so TI cannot complete against it. [Contract §11](interface-contract.md#11-network-api-contracts) retains field rules and examples.

### K.3 Synchronous Query and Subscriber Routing

@@ -324,7 +324,7 @@ Specification links identify the current rule or open question. Code evidence re
| Claim | Spec/Decision Evidence | Code Evidence | Implementation Impact | Decision Needed |
|---|---|---|---|---|
| **C01** SRM owns QoD expiry; OEG runs no timer | [ADR-0036](/decisions/0036-srm-owns-network-capability-expiry.md); [§K.1](#k1-quality-on-demand), [contract §9](interface-contract.md#9-data-dictionary) | OEG main.py:211–293; services/quality_on_demand_service.py:709–799 | OEG expires AVAILABLE sessions itself and later cleans NETWORK_TERMINATED sessions after 360 s | Choose expiry owner, duplicate suppression and migration; do not run competing schedulers by assumption |
| **C02** TI resolves the application registration as its specification; no seed | [ADR-0035](/decisions/0035-qod-session-service-specification.md); [§K.2](#k2-traffic-influence) | OEG services/traffic_influence_service.py:266–285; SRM application/bootstrap/platform_catalog.py:42–53 | OEG checks app ownership but publishes its configured TI seed; selected SRM seeds only QoD/location | Choose TI specification model and deploy a matching catalog contract before integration |
| **C02** TI uses a platform-seeded specification, not the application registration | [ADR-0035](/decisions/0035-qod-session-service-specification.md) amendment; [§K.2](#k2-traffic-influence) | OEG services/traffic_influence_service.py:266–285; SRM application/bootstrap/platform_catalog.py:42–53 | OEG publishes its configured TI seed, as decided; the referenced SRM revision seeds only QoD/location | Move the SRM reference to a revision that seeds `traffic-influence` |
| **C03** Atomic command staging and broker-acknowledged EAM acceptance | [§M.2](#m2-commit-and-publish-boundary); [§J.4](#j4-termination) | OEG dependencies.py:216–223; repos/operations.py:37–48; EAM service:574–607 | Outbox and broker acknowledgment are required but absent: flush/publish precede request commit; command can escape rollback; teardown can partially publish | Outbox and acknowledgment-before-202 are required. Failure state, outward error, wait/retry limits, recovery, worker concurrency and multi-command/inline termination remain Unspecified. |
| **C04** Durable at-least-once DataBus delivery | [ADR-0022](/decisions/0022-nats-jetstream-async-messaging.md); [contract §2](interface-contract.md#2-databus-contracts); [SRM contract](/srm/interface-contract.md) | OEG adapters/databus/nats_adapter.py:67–77,99–105,143–145; tests/unit/test_eam_contract.py:252–277; SRM NATS adapters | Core NATS publish/subscribe, no durable consumer/ack; malformed/handler-error messages logged and dropped; durable test skipped | Implement or explicitly revise durable transport; outage/restart loss is not cured by force-failure reconciliation |
| **C05** Provider ownership and organization sharing | [§I.1](#i1-authentication-and-ownership); [overview F5](overview-and-architecture.md#f-open-questions); [AI² boundary](/ai2/overview-and-architecture.md#d-boundaries) | OEG EAM service:367–382,790,1099,1667–1683; QoD service:599–609; TI ownership helper | Selected code checks provider, hides others with 404; tenant-wide sharing is not thereby supported | Define organization-wide sharing without weakening provider ownership by assumption. OEG and AI² document the same current provider checks. |
+1 −1
Changes for srm/architecture.md: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ clean boundaries.
| JSON Schema validation (REQ-SRM-12) | Validate all `command.srm.*` inbound and `event.srm.operation.*` outbound against published schemas |
| Reconciliation (REQ-SRM-11) | Periodic; emit `event.srm.inventory.drift_detected` on divergence |
| Scale / terminate | Same async `command.srm.service.scale` / `.terminate` path as deploy |
| Network capability | `command.srm.network.capability.*` for async lifecycle/session (QoD, Traffic Influence) |d
| Network capability | `command.srm.network.capability.*` for async lifecycle/session (QoD, Traffic Influence) |
| Capability expiry | SRM enforces a realization's requested duration and reports lapse as a backend-driven `event.srm.operation.status` (`metadata.capability_status_info=duration_expired`); OEG runs no timer (ADR-0036). Mechanism is an implementation choice — scheduled sweep, backend notification, or reconciliation |
| Sync query/data | Internal HTTP for Device Location/Status; no inventory |
| QoS / capability profiles | Referenced by `profile_ref` in canonical params — unvalidated today; validation, when added, is a catalog-layer specification per offering (ADR-0035) |