From f8fde35e3ea23afec9d2999461a3a1387227dcb1 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 15 Jun 2026 22:48:31 +0200 Subject: [PATCH] Implement ops-hub evidence intake contracts --- .../ops-hub-activity-core-event-payloads.md | 220 ++++++++++++++++++ .../ops-hub-activity-core-mapping.md | 206 ++++++++++++++++ ...s-hub-activity-core-fallback-validation.md | 95 ++++++++ .../ops-hub-evidence-intake-current-state.md | 157 +++++++++++++ .../IHUB-WP-0022-ops-hub-evidence-intake.md | 69 ++++-- 5 files changed, 732 insertions(+), 15 deletions(-) create mode 100644 docs/contracts/ops-hub-activity-core-event-payloads.md create mode 100644 docs/contracts/ops-hub-activity-core-mapping.md create mode 100644 docs/evidence/ops-hub-activity-core-fallback-validation.md create mode 100644 docs/research/ops-hub-evidence-intake-current-state.md diff --git a/docs/contracts/ops-hub-activity-core-event-payloads.md b/docs/contracts/ops-hub-activity-core-event-payloads.md new file mode 100644 index 0000000..2e6e5e1 --- /dev/null +++ b/docs/contracts/ops-hub-activity-core-event-payloads.md @@ -0,0 +1,220 @@ +# Ops Hub Activity-Core Event Payloads + +Date: 2026-06-15 + +Workplan: `IHUB-WP-0022` + +## Inter-Hub Request Shape + +Activity-core should submit ops evidence through: + +```text +POST /api/v2/interaction-events +Authorization: Bearer ${OPS_HUB_KEY} +Content-Type: application/json +``` + +Each request body must use the Inter-Hub v2 interaction-event shape: + +```json +{ + "widgetId": "", + "eventType": "ops-endpoint-verified", + "viewContext": "ops-inventory-probe", + "metadata": { + "type": "ops-endpoint-verified", + "version": "1.0", + "publisher": "activity-core", + "attributes": {} + } +} +``` + +Inter-Hub sets `occurredAt` on receipt. Activity-core must send the actual +probe timestamp as `metadata.attributes.observed_at`. + +## Shared Rules + +- `widgetId` must be a UUID for an existing ops-hub widget. +- `eventType` must exist in Inter-Hub's event type registry. +- If the API consumer is bound to an active ops-hub manifest, `eventType` must + be declared by that manifest. +- `viewContext` should be `ops-inventory-probe` unless a more specific context + is useful, such as `ops-inventory-probe/endpoints`. +- `metadata.type` must match the Inter-Hub `eventType`. +- `metadata.version` must match the activity-core event definition version. +- `metadata.publisher` must be `activity-core`. +- `metadata.attributes.idempotency_key` is required, even though Inter-Hub does + not currently enforce idempotency. +- Duplicate tolerance is required on the reader side until Inter-Hub provides a + unique idempotency constraint. +- Payloads must not include secrets, authorization headers, cookies, token-like + values, private key material, raw response bodies, command output, or + unredacted URL query strings. + +## Status Vocabulary + +Use the activity-core status vocabulary: + +- `ok` +- `degraded` +- `down` +- `skipped` + +Use `reason` for compact machine-readable explanations, for example: + +- `expected_status_mismatch` +- `expected_signal_missing` +- `unsupported_access_path_type` +- `backup_probe_not_implemented` +- `missing_endpoint` + +## Example: Service Observed + +```json +{ + "widgetId": "", + "eventType": "ops-service-observed", + "viewContext": "ops-inventory-probe/services", + "metadata": { + "type": "ops-service-observed", + "version": "1.0", + "publisher": "activity-core", + "attributes": { + "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc", + "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:state-hub:ops-service-observed", + "service_id": "state-hub", + "service_name": "State Hub", + "environment": "local", + "lifecycle_state": "observed", + "observed_status": "ok", + "observed_at": "2026-06-05T10:15:01Z" + } + } +} +``` + +## Example: Endpoint Verified + +```json +{ + "widgetId": "", + "eventType": "ops-endpoint-verified", + "viewContext": "ops-inventory-probe/endpoints", + "metadata": { + "type": "ops-endpoint-verified", + "version": "1.0", + "publisher": "activity-core", + "attributes": { + "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc", + "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-endpoint-verified", + "service_id": "gitea", + "endpoint_id": "gitea-oci-registry", + "endpoint_type": "https", + "endpoint_url": "https://gitea.coulomb.social/v2/", + "expected_status": 401, + "status_code": 401, + "matched_expected_status": true, + "matched_expected_signal": true, + "observed_status": "ok", + "observed_at": "2026-06-05T10:15:01Z", + "widget_ref": "ops:endpoint:gitea-registry" + } + } +} +``` + +## Example: Access Path Checked + +```json +{ + "widgetId": "", + "eventType": "ops-access-path-checked", + "viewContext": "ops-inventory-probe/access-paths", + "metadata": { + "type": "ops-access-path-checked", + "version": "1.0", + "publisher": "activity-core", + "attributes": { + "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc", + "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-access-1:ops-access-path-checked", + "service_id": "gitea", + "access_path_id": "gitea-access-1", + "access_path_type": "k8s", + "declared_status": "unknown", + "observed_status": "skipped", + "reason": "unsupported_access_path_type", + "observed_at": "2026-06-05T10:15:01Z" + } + } +} +``` + +## Example: Backup Verified + +```json +{ + "widgetId": "", + "eventType": "ops-backup-verified", + "viewContext": "ops-inventory-probe/backups", + "metadata": { + "type": "ops-backup-verified", + "version": "1.0", + "publisher": "activity-core", + "attributes": { + "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc", + "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:database:gitea-db:ops-backup-verified", + "service_id": "gitea", + "backing_store_ref": "database:gitea-db", + "backup_evidence_ref": "state-hub:progress:", + "restore_verified": false, + "observed_status": "skipped", + "reason": "backup_probe_not_implemented", + "observed_at": "2026-06-05T10:15:01Z" + } + } +} +``` + +## Example: Inventory Drift + +```json +{ + "widgetId": "", + "eventType": "ops-inventory-drift", + "viewContext": "ops-inventory-probe/drift", + "metadata": { + "type": "ops-inventory-drift", + "version": "1.0", + "publisher": "activity-core", + "attributes": { + "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc", + "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-inventory-drift", + "service_id": "gitea", + "inventory_object_id": "gitea-oci-registry", + "drift_kind": "status_mismatch", + "declared_summary": "expected_status=401", + "observed_summary": "status_code=200", + "observed_status": "degraded", + "reason": "expected_status_mismatch", + "observed_at": "2026-06-05T10:15:01Z" + } + } +} +``` + +## Expected API Errors + +Activity-core should treat these as configuration or rollout errors: + +| Error | Meaning | Recovery | +|---|---|---| +| `401` | Missing or invalid `OPS_HUB_KEY` | Check Secret provisioning; do not log the key. | +| `422` with `unregistered_event_type` | Event type not in Inter-Hub registry | Activate the ops-hub manifest vocabulary. | +| `422` with `event_type_not_in_manifest` | Runtime consumer manifest does not declare the event | Bind the consumer to the active manifest or activate a corrected manifest. | +| `422` with `Widget not found` | Mapping points at a missing widget | Refresh `OPS_HUB_WIDGET_MAPPING`. | +| `422` with `unregistered_policy_scope` during widget seed | Policy scope is absent | Declare and activate `ops-evidence`. | + +For the first activity-core slice, a failed Inter-Hub submission should not +fail the whole probe if State Hub fallback posting succeeds. It should return a +compact sink result naming the non-secret failure class. diff --git a/docs/contracts/ops-hub-activity-core-mapping.md b/docs/contracts/ops-hub-activity-core-mapping.md new file mode 100644 index 0000000..986b979 --- /dev/null +++ b/docs/contracts/ops-hub-activity-core-mapping.md @@ -0,0 +1,206 @@ +# Ops Hub Activity-Core Widget Mapping + +Date: 2026-06-15 + +Workplan: `IHUB-WP-0022` + +## Purpose + +`OPS_HUB_WIDGET_MAPPING` tells activity-core which Inter-Hub widget receives +each ops evidence event. The value must be non-secret JSON. It may contain +Inter-Hub widget UUIDs and logical references, but it must never contain +`OPS_HUB_KEY` or any operator credential. + +Activity-core currently only checks that a mapping value is present before +returning `inter_hub_sink_deferred`. This document defines the contract that +the future Inter-Hub submission implementation should parse. + +## Versioned Shape + +```json +{ + "version": "ops-hub.activity-core.widget-mapping.v1", + "hub": { + "slug": "ops-hub", + "id": "" + }, + "policyScope": "ops-evidence", + "defaultViewContext": "ops-inventory-probe", + "events": { + "ops-service-observed": { + "family": "services", + "aggregateWidgetRef": "ops:service:aggregate" + }, + "ops-endpoint-verified": { + "family": "endpoints", + "aggregateWidgetRef": "ops:endpoint:aggregate" + }, + "ops-access-path-checked": { + "family": "accessPaths", + "aggregateWidgetRef": "ops:access-path:aggregate" + }, + "ops-backup-verified": { + "family": "backups", + "aggregateWidgetRef": "ops:backup:aggregate" + }, + "ops-inventory-drift": { + "family": "drift", + "aggregateWidgetRef": "ops:drift:aggregate" + } + }, + "widgets": { + "aggregate": { + "ops:service:aggregate": { + "widgetId": "", + "widgetType": "ops-service-card", + "name": "Ops Service Evidence Intake" + }, + "ops:endpoint:aggregate": { + "widgetId": "", + "widgetType": "ops-endpoint-card", + "name": "Ops Endpoint Evidence Intake" + }, + "ops:access-path:aggregate": { + "widgetId": "", + "widgetType": "ops-access-path-card", + "name": "Ops Access Path Evidence Intake" + }, + "ops:backup:aggregate": { + "widgetId": "", + "widgetType": "ops-backup-card", + "name": "Ops Backup Evidence Intake" + }, + "ops:drift:aggregate": { + "widgetId": "", + "widgetType": "ops-drift-card", + "name": "Ops Inventory Drift Evidence Intake" + } + }, + "services": { + "state-hub": { + "widgetRef": "ops:service:state-hub", + "widgetId": "" + } + }, + "endpoints": { + "gitea:gitea-oci-registry": { + "widgetRef": "ops:endpoint:gitea-registry", + "widgetId": "" + } + }, + "accessPaths": { + "gitea:gitea-access-1": { + "widgetRef": "ops:access-path:gitea-access-1", + "widgetId": "" + } + }, + "backups": { + "gitea:database:gitea-db": { + "widgetRef": "ops:backup:gitea-db", + "widgetId": "" + } + }, + "drift": { + "gitea:gitea-oci-registry": { + "widgetRef": "ops:drift:gitea-oci-registry", + "widgetId": "" + } + } + } +} +``` + +## Selector Rules + +Activity-core should choose a widget in this order: + +1. If the evidence payload carries a `widget_ref` and that reference exists in + the mapping, use it. +2. For `ops-service-observed`, use `services[""]`. +3. For `ops-endpoint-verified`, use + `endpoints[":"]`. +4. For `ops-access-path-checked`, use + `accessPaths[":"]`. +5. For `ops-backup-verified`, use + `backups[":"]`. +6. For `ops-inventory-drift`, use + `drift[":"]`. +7. If no entity-specific widget exists, use the event's aggregate widget. +8. If neither an entity-specific nor aggregate widget exists, skip Inter-Hub + submission with a non-secret result that names the missing selector. + +## Bootstrap Widget Names + +The initial aggregate widgets should be seeded before activity-core is pointed +at Inter-Hub: + +| Widget ref | Widget type | Suggested name | +|---|---|---| +| `ops:service:aggregate` | `ops-service-card` | Ops Service Evidence Intake | +| `ops:endpoint:aggregate` | `ops-endpoint-card` | Ops Endpoint Evidence Intake | +| `ops:access-path:aggregate` | `ops-access-path-card` | Ops Access Path Evidence Intake | +| `ops:backup:aggregate` | `ops-backup-card` | Ops Backup Evidence Intake | +| `ops:drift:aggregate` | `ops-drift-card` | Ops Inventory Drift Evidence Intake | + +Per-entity widgets may be seeded later without changing the event contract. +When a per-entity widget is added, update the mapping and keep the aggregate +widget as the fallback. + +## Compatibility Rules + +- `version` is required. Reject unknown major versions. +- Consumers must tolerate additional fields. +- Widget UUIDs may rotate, but `widgetRef` values should remain stable. +- Removing a widget mapping is a breaking change for that selector unless the + aggregate fallback remains present. +- Mapping updates must be deployed before activity-core starts sending events + that depend on the new selectors. +- The mapping is non-secret and may be stored in a ConfigMap or environment + variable. `OPS_HUB_KEY` must remain Secret-only. + +## Minimum Valid Mapping + +For the first live smoke, an aggregate-only mapping is enough: + +```json +{ + "version": "ops-hub.activity-core.widget-mapping.v1", + "hub": { + "slug": "ops-hub", + "id": "" + }, + "policyScope": "ops-evidence", + "defaultViewContext": "ops-inventory-probe", + "events": { + "ops-service-observed": { + "family": "services", + "aggregateWidgetRef": "ops:service:aggregate" + }, + "ops-endpoint-verified": { + "family": "endpoints", + "aggregateWidgetRef": "ops:endpoint:aggregate" + }, + "ops-access-path-checked": { + "family": "accessPaths", + "aggregateWidgetRef": "ops:access-path:aggregate" + }, + "ops-backup-verified": { + "family": "backups", + "aggregateWidgetRef": "ops:backup:aggregate" + }, + "ops-inventory-drift": { + "family": "drift", + "aggregateWidgetRef": "ops:drift:aggregate" + } + }, + "widgets": { + "aggregate": { + "ops:service:aggregate": { "widgetId": "" }, + "ops:endpoint:aggregate": { "widgetId": "" }, + "ops:access-path:aggregate": { "widgetId": "" }, + "ops:backup:aggregate": { "widgetId": "" }, + "ops:drift:aggregate": { "widgetId": "" } + } + } +} +``` diff --git a/docs/evidence/ops-hub-activity-core-fallback-validation.md b/docs/evidence/ops-hub-activity-core-fallback-validation.md new file mode 100644 index 0000000..d58626a --- /dev/null +++ b/docs/evidence/ops-hub-activity-core-fallback-validation.md @@ -0,0 +1,95 @@ +# Ops Hub Activity-Core Fallback Validation + +Date: 2026-06-15 + +Workplan: `IHUB-WP-0022` + +## Validation Result + +The State Hub fallback path is implemented and tested in activity-core, but no +live fallback event exists in State Hub yet. + +Direct query: + +```text +GET http://127.0.0.1:8000/progress/?event_type=ops_inventory_probe&limit=20 +``` + +Observed result on 2026-06-15: + +```json +[] +``` + +This means Inter-Hub can accept the fallback-first design, but cannot yet cite +live `ops_inventory_probe` evidence as a closure artifact. + +## What Is Validated + +Activity-core local tests validate the fallback sink shape: + +- `state-hub-progress` posts one compact `ops_inventory_probe` progress event + per run. +- The fallback idempotency key is stable: + `activity_core_run_id:context_key:ops_inventory_probe`. +- The posted summary is compact, for example: + `Ops inventory probe: 1 ok, 0 degraded, 0 down, 1 skipped`. +- The detail includes `activity_id`, `activity_core_run_id`, `scheduled_for`, + `source_type`, `context_key`, `idempotency_key`, and a compact `probe` + payload. +- The compact probe strips response bodies, authorization headers, credentials, + URL query strings, and token-like values. +- Inter-Hub sink config absence is a clean skip with + `reason = missing_inter_hub_config`. +- When config is present but submission is not implemented, the result is a + clean skip with `reason = inter_hub_sink_deferred`. + +## What Is Not Yet Validated + +- No live activity-core worker has posted an `ops_inventory_probe` event to the + local State Hub instance. +- No live Inter-Hub event has been submitted from activity-core. +- No production `OPS_HUB_KEY` handoff has been verified. +- No `OPS_HUB_WIDGET_MAPPING` has been deployed into the activity-core runtime. +- No per-entity widget mapping has been smoke-tested against production + ops-hub widgets. + +## Gaps Compared With Inter-Hub Submission + +State Hub fallback is useful as continuity evidence, but it is not a full +replacement for Inter-Hub submission: + +- It records one compact run summary, not one governed widget event per entity. +- It cannot attach annotations directly to the affected service or endpoint + widget. +- It does not prove ops-hub manifest vocabulary enforcement. +- It does not prove API consumer, key, rate-limit, or widget mapping behavior. +- It does not populate Inter-Hub event lists for hub dashboards or downstream + widget governance workflows. + +## Closure Recommendation + +`ACTIVITY-WP-0007/T06` should not close as fully activated based on the current +State Hub state, because no live `ops_inventory_probe` progress event exists. + +Two acceptable closure paths remain: + +1. Fallback-deferred closure: run one disabled/manual activity-core probe, + confirm a live non-secret `ops_inventory_probe` progress event in State Hub, + and explicitly record that Inter-Hub submission is deferred until + `IHUB-WP-0022-T03/T04/T07` complete. +2. Full Inter-Hub closure: provision `OPS_HUB_KEY`, deploy + `OPS_HUB_WIDGET_MAPPING`, seed/verify the ops-hub widgets, and submit one + accepted Inter-Hub event per activity-core event type. + +Until one of those paths is satisfied, Inter-Hub should keep the activity-core +closure gate open. + +## Next Evidence To Capture + +- Progress id for the first live `ops_inventory_probe` event. +- Non-secret summary counts from that progress detail. +- Confirmation that Inter-Hub sink remains skipped cleanly while config is + absent or deferred. +- After ops-hub activation, event ids for one accepted Inter-Hub submission per + event type. diff --git a/docs/research/ops-hub-evidence-intake-current-state.md b/docs/research/ops-hub-evidence-intake-current-state.md new file mode 100644 index 0000000..7f5edf0 --- /dev/null +++ b/docs/research/ops-hub-evidence-intake-current-state.md @@ -0,0 +1,157 @@ +# Ops Hub Evidence Intake - Current State + +Date: 2026-06-15 + +Workplan: `IHUB-WP-0022` + +## Summary + +Inter-Hub has the generic v2 API surface needed for activity-core evidence +intake, but the activity-core path is not live yet. The safe implementation +slice is therefore contract-first: + +- document the ops-hub widget mapping shape; +- document the Inter-Hub event payload shape; +- keep `OPS_HUB_KEY` outside Git; +- accept State Hub fallback as the temporary safety path; +- wait on live ops-hub manifest/widgets, key provisioning, and production + smoke before enabling per-entity Inter-Hub submission. + +## Inter-Hub API Surface + +The current repo supports the necessary primitives through `/api/v2`. + +`Web.Controller.Api.V2.Widgets`: + +- `GET /api/v2/widgets` is authenticated and paginated. +- `POST /api/v2/widgets` requires `hubId`, `name`, and `widgetType`. +- Optional fields are `capabilityRef`, `viewContext`, `policyScope`, + `status`, and `adapterSpecId`. +- `policyScope` defaults to `internal` when omitted. +- Valid widget statuses are `active`, `deprecated`, and `draft`. +- Widget type and policy scope are validated through the type registries. +- Widget creation creates an initial `WidgetVersion` snapshot. + +`Web.Controller.Api.V2.InteractionEvents`: + +- `GET /api/v2/interaction-events` is authenticated and paginated. +- Supported list filters are `widgetId` and `eventType`. +- `POST /api/v2/interaction-events` requires `widgetId` and `eventType`. +- `viewContext` is optional and is persisted as `viewContextRef`. +- `metadata` is accepted as a JSON object when the request content type is + `application/json`. +- The event type must exist in `event_type_registry`. +- If the API consumer is bound to an active manifest, the event type must also + be declared by that manifest. +- `occurredAt` is server-set. Activity-core should send its observed timestamp + inside `metadata.attributes.observed_at`. +- Actor attribution is `actorType = "api"` for this endpoint. + +`docs/new-hub-quickstart.md` and `scripts/ops-hub-bootstrap-smoke.py` already +show the bootstrap shape for a single ops-hub endpoint event. The activity-core +intake needs that pattern expanded from one smoke widget/event to a durable +five-event contract. + +## Activity-Core Contract + +The neighboring `activity-core` repo already defines the intended event +vocabulary under `event-types/`: + +- `ops-service-observed` +- `ops-endpoint-verified` +- `ops-access-path-checked` +- `ops-backup-verified` +- `ops-inventory-drift` + +The current activity-core sink implementation is intentionally conservative: + +- `state-hub-progress` is implemented and idempotent. +- It posts `ops_inventory_probe` progress with compact non-secret detail. +- The idempotency key is `activity_core_run_id + context_key + event_type`. +- The compact probe strips raw response bodies, headers, credentials, URL query + strings, and token-like material. +- Inter-Hub sink names are recognized, but the sink currently returns + `missing_inter_hub_config` or `inter_hub_sink_deferred`; it does not submit + events yet. +- Inter-Hub mode requires `INTER_HUB_URL`, `OPS_HUB_KEY`, and either + `OPS_HUB_WIDGET_MAPPING`, `widget_mapping`, or `capability_mapping`. + +Activity-core deployment placeholders exist in +`activity-core/k8s/railiance/20-runtime.yaml`: + +- `INTER_HUB_URL` is present but empty. +- `OPS_HUB_WIDGET_MAPPING` is present but empty. +- `OPS_HUB_KEY` is created only as an empty Secret placeholder by + `bootstrap-secrets.sh`. + +## Fallback Evidence State + +State Hub was queried directly for live fallback evidence: + +```text +GET http://127.0.0.1:8000/progress/?event_type=ops_inventory_probe&limit=20 +``` + +Result on 2026-06-15: an empty list. + +That means the fallback sink is implemented and tested in activity-core, but no +live `ops_inventory_probe` progress event is available for Inter-Hub to accept +as closure evidence yet. + +## Production Gates + +Known gates before per-entity Inter-Hub submission can be treated as live: + +1. The production Inter-Hub deployment must include commit `5101eb5` or an + equivalent fix for PostgreSQL `COUNT(*)` decoding in widget creation and + API rate-limit reads. +2. The active `ops-hub` manifest must declare the five activity-core event + types, the selected widget types, the annotation category, and the policy + scope. +3. Seed widgets named by the mapping contract must exist in the target + environment. +4. `OPS_HUB_KEY` must be provisioned outside Git, preferably in OpenBao at + `platform/operators/ops-hub/runtime`, field `OPS_HUB_KEY`. +5. Activity-core must receive `INTER_HUB_URL`, `OPS_HUB_KEY`, and + `OPS_HUB_WIDGET_MAPPING` through its runtime config/Secret path. +6. A controlled smoke must submit one event for each declared event type and + verify that an undeclared event type is rejected. + +## Recommended Manifest Vocabulary + +Use one policy scope for the first slice: + +- `ops-evidence` + +Use one annotation category: + +- `ops-risk` + +Use these widget types unless the operator prefers to keep a smaller aggregate +surface: + +- `ops-service-card` +- `ops-endpoint-card` +- `ops-access-path-card` +- `ops-backup-card` +- `ops-drift-card` + +Use the activity-core event types exactly as published: + +- `ops-service-observed` +- `ops-endpoint-verified` +- `ops-access-path-checked` +- `ops-backup-verified` +- `ops-inventory-drift` + +## Open Questions + +- Does ops-hub already have a production manifest that should be patched rather + than replaced? +- Should the first production mapping use only aggregate widgets, or seed + per-entity widgets for the known Railiance inventory? +- Which OpenBao or cluster Secret path should activity-core consume for + `OPS_HUB_KEY`? +- Should activity-core close `ACTIVITY-WP-0007/T06` after a live State Hub + fallback event with explicit Inter-Hub deferral, or only after real + Inter-Hub submission? diff --git a/workplans/IHUB-WP-0022-ops-hub-evidence-intake.md b/workplans/IHUB-WP-0022-ops-hub-evidence-intake.md index 28535e2..047b444 100644 --- a/workplans/IHUB-WP-0022-ops-hub-evidence-intake.md +++ b/workplans/IHUB-WP-0022-ops-hub-evidence-intake.md @@ -4,7 +4,7 @@ type: workplan title: "Ops Hub Evidence Intake for Activity Core" domain: inter_hub repo: inter-hub -status: ready +status: active owner: codex topic_slug: inter_hub created: "2026-06-15" @@ -115,7 +115,7 @@ split into per-entity widgets after payload evidence proves stable. ```task id: IHUB-WP-0022-T01 -status: todo +status: done priority: high state_hub_task_id: "f9006504-e5f5-465f-9588-3f4279d12b84" ``` @@ -137,13 +137,20 @@ Answer: Exit criteria: `docs/research/ops-hub-evidence-intake-current-state.md` exists with non-secret findings and open gates. +Implementation note (2026-06-15): completed +`docs/research/ops-hub-evidence-intake-current-state.md`. The audit confirms +that Inter-Hub has the required v2 widget and interaction-event primitives, +activity-core has the five event definitions and a tested State Hub fallback +sink, but the Inter-Hub sink is still deferred and no live +`ops_inventory_probe` progress event exists in State Hub yet. + --- ### T02 - Define the ops-hub evidence mapping contract ```task id: IHUB-WP-0022-T02 -status: todo +status: done priority: high depends_on: T01 state_hub_task_id: "4f8a98b9-0d01-4333-b847-f83b8c85a5ab" @@ -166,13 +173,19 @@ The contract must specify: Exit criteria: `docs/contracts/ops-hub-activity-core-mapping.md` documents the mapping in copyable JSON examples without containing secrets. +Implementation note (2026-06-15): completed +`docs/contracts/ops-hub-activity-core-mapping.md`. The contract defines a +versioned non-secret `OPS_HUB_WIDGET_MAPPING` JSON shape, aggregate-first +fallback widgets, per-entity selector rules, stable `widgetRef` values, and +Secret-only handling for `OPS_HUB_KEY`. + --- ### T03 - Prepare manifest vocabulary and seed widgets ```task id: IHUB-WP-0022-T03 -status: todo +status: wait priority: high depends_on: T02 state_hub_task_id: "94fc9806-781c-45f6-a43c-a6bce13da47b" @@ -200,6 +213,11 @@ Exit criteria: target environment. - Non-secret widget IDs or logical names are recorded in the mapping contract. +Current wait reason (2026-06-15): manifest/widget activation needs a target +environment with the `5101eb5` COUNT decode fix live and an authenticated +operator/runtime key path. The required vocabulary is documented, but no live +manifest or widget seed was performed in this implementation slice. + --- ### T04 - Provision the runtime API key outside Git @@ -241,7 +259,7 @@ root/sudo-capable token handoff or operator UI action. ```task id: IHUB-WP-0022-T05 -status: todo +status: done priority: high depends_on: T02 state_hub_task_id: "4eb04a83-8eea-4cab-861c-a39f312a5bb9" @@ -265,13 +283,19 @@ The document must cover: Exit criteria: `docs/contracts/ops-hub-activity-core-event-payloads.md` exists with one example payload for each of the five event types. +Implementation note (2026-06-15): completed +`docs/contracts/ops-hub-activity-core-event-payloads.md`. It documents the +Inter-Hub request envelope, shared validation rules, idempotency expectations, +forbidden payload material, expected API errors, and one example for each +activity-core event type. + --- ### T06 - Validate fallback-first intake ```task id: IHUB-WP-0022-T06 -status: todo +status: wait priority: medium depends_on: T01 state_hub_task_id: "38b54991-bed2-4f9d-bede-bea35821b1ef" @@ -294,13 +318,20 @@ Validation path: Exit criteria: `docs/evidence/ops-hub-activity-core-fallback-validation.md` records fallback evidence, gaps, and the closure recommendation. +Implementation note (2026-06-15): created +`docs/evidence/ops-hub-activity-core-fallback-validation.md`. Activity-core's +fallback sink is implemented and locally tested, but a direct State Hub query +for `event_type=ops_inventory_probe` returned no live events. This task remains +waiting on a disabled/manual activity-core probe or other live fallback +evidence before it can close. + --- ### T07 - Run end-to-end Inter-Hub submission smoke ```task id: IHUB-WP-0022-T07 -status: todo +status: wait priority: high depends_on: T03,T04,T05 state_hub_task_id: "23baee9b-d710-42c8-9a19-f936bd237444" @@ -326,13 +357,17 @@ Smoke checks: Exit criteria: non-secret smoke evidence is recorded and activity-core can enable the Inter-Hub sink in its controlled environment. +Current wait reason (2026-06-15): depends on live manifest/widgets from T03, +runtime key provisioning from T04, and activity-core implementing actual +Inter-Hub submission beyond its current deferred sink. + --- ### T08 - Coordinate ACTIVITY-WP-0007 closure handoff ```task id: IHUB-WP-0022-T08 -status: todo +status: wait priority: medium depends_on: T06,T07 state_hub_task_id: "4a7ed0ed-552e-42d3-a90f-1efd52b8851e" @@ -354,18 +389,22 @@ The handoff must state: Exit criteria: activity-core has enough non-secret evidence and configuration shape to close or unblock `ACTIVITY-WP-0007/T06`. +Current wait reason (2026-06-15): closure handoff depends on either a live +State Hub fallback event plus an explicit Inter-Hub deferral decision, or a +successful Inter-Hub submission smoke. + ## Exit Criteria Summary | Task | Deliverable | Status | |---|---|---| -| T01 | `docs/research/ops-hub-evidence-intake-current-state.md` | todo | -| T02 | `docs/contracts/ops-hub-activity-core-mapping.md` | todo | -| T03 | Active ops-hub manifest and seed widgets | todo | +| T01 | `docs/research/ops-hub-evidence-intake-current-state.md` | done | +| T02 | `docs/contracts/ops-hub-activity-core-mapping.md` | done | +| T03 | Active ops-hub manifest and seed widgets | wait | | T04 | `OPS_HUB_KEY` stored outside Git and smokeable | wait | -| T05 | `docs/contracts/ops-hub-activity-core-event-payloads.md` | todo | -| T06 | `docs/evidence/ops-hub-activity-core-fallback-validation.md` | todo | -| T07 | End-to-end Inter-Hub submission smoke evidence | todo | -| T08 | activity-core closure handoff | todo | +| T05 | `docs/contracts/ops-hub-activity-core-event-payloads.md` | done | +| T06 | `docs/evidence/ops-hub-activity-core-fallback-validation.md` | wait | +| T07 | End-to-end Inter-Hub submission smoke evidence | wait | +| T08 | activity-core closure handoff | wait | ## Binding Principles