generated from coulomb/repo-seed
Align ops-hub activity-core contract vocabulary
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Ops Hub Activity-Core Event Payloads
|
||||
|
||||
Date: 2026-06-15
|
||||
Updated: 2026-06-27
|
||||
|
||||
Workplan: `IHUB-WP-0022`
|
||||
|
||||
@@ -33,6 +34,28 @@ Each request body must use the Inter-Hub v2 interaction-event shape:
|
||||
Inter-Hub sets `occurredAt` on receipt. Activity-core must send the actual
|
||||
probe timestamp as `metadata.attributes.observed_at`.
|
||||
|
||||
## Live Vocabulary Alignment
|
||||
|
||||
Use event names declared by the live ops-hub manifest:
|
||||
|
||||
- `ops-service-discovered`
|
||||
- `ops-endpoint-verified`
|
||||
- `ops-backup-verified`
|
||||
- `ops-drift-detected`
|
||||
|
||||
Transitional aliases may be accepted in local mapping logic but should not be
|
||||
submitted to Inter-Hub:
|
||||
|
||||
| Earlier activity-core name | Live event name |
|
||||
| --- | --- |
|
||||
| `ops-service-observed` | `ops-service-discovered` |
|
||||
| `ops-inventory-drift` | `ops-drift-detected` |
|
||||
|
||||
`ops-access-path-checked` is deferred because the live manifest has no
|
||||
access-path event type or access-path widget type. Keep State Hub fallback
|
||||
posting for access-path evidence until ops-hub adds that vocabulary or an
|
||||
operator decision maps it to `ops-readiness-gate-updated` or `ops-risk-raised`.
|
||||
|
||||
## Shared Rules
|
||||
|
||||
- `widgetId` must be a UUID for an existing ops-hub widget.
|
||||
@@ -41,7 +64,7 @@ probe timestamp as `metadata.attributes.observed_at`.
|
||||
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.type` must match the Inter-Hub `eventType` after alias translation.
|
||||
- `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
|
||||
@@ -69,26 +92,27 @@ Use `reason` for compact machine-readable explanations, for example:
|
||||
- `backup_probe_not_implemented`
|
||||
- `missing_endpoint`
|
||||
|
||||
## Example: Service Observed
|
||||
## Example: Service Discovered
|
||||
|
||||
```json
|
||||
{
|
||||
"widgetId": "<service-widget-uuid>",
|
||||
"eventType": "ops-service-observed",
|
||||
"widgetId": "<service-or-catalog-widget-uuid>",
|
||||
"eventType": "ops-service-discovered",
|
||||
"viewContext": "ops-inventory-probe/services",
|
||||
"metadata": {
|
||||
"type": "ops-service-observed",
|
||||
"type": "ops-service-discovered",
|
||||
"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",
|
||||
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:state-hub:ops-service-discovered",
|
||||
"service_id": "state-hub",
|
||||
"service_name": "State Hub",
|
||||
"environment": "local",
|
||||
"lifecycle_state": "observed",
|
||||
"lifecycle_state": "discovered",
|
||||
"observed_status": "ok",
|
||||
"observed_at": "2026-06-05T10:15:01Z"
|
||||
"observed_at": "2026-06-05T10:15:01Z",
|
||||
"widget_ref": "ops:service:state-hub"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,37 +148,26 @@ Use `reason` for compact machine-readable explanations, for example:
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Access Path Checked
|
||||
## Deferred: Access Path Checked
|
||||
|
||||
```json
|
||||
{
|
||||
"widgetId": "<access-path-widget-uuid>",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Do not submit `ops-access-path-checked` to Inter-Hub while the live ops-hub
|
||||
manifest lacks that event type and a matching widget type. Activity-core should
|
||||
continue to include compact access-path detail in State Hub fallback summaries
|
||||
and return a non-secret deferred sink result for Inter-Hub.
|
||||
|
||||
If the operator chooses to represent access-path state as readiness or risk,
|
||||
create a separate mapping decision and send the supported event type instead of
|
||||
silently rewriting access-path evidence.
|
||||
|
||||
## Example: Backup Verified
|
||||
|
||||
This event is declared by the live manifest, but it needs a target
|
||||
`ops-backup-set` widget such as `ops:backup-set:aggregate` before the smoke can
|
||||
submit it.
|
||||
|
||||
```json
|
||||
{
|
||||
"widgetId": "<backup-widget-uuid>",
|
||||
"widgetId": "<backup-set-widget-uuid>",
|
||||
"eventType": "ops-backup-verified",
|
||||
"viewContext": "ops-inventory-probe/backups",
|
||||
"metadata": {
|
||||
@@ -170,26 +183,27 @@ Use `reason` for compact machine-readable explanations, for example:
|
||||
"restore_verified": false,
|
||||
"observed_status": "skipped",
|
||||
"reason": "backup_probe_not_implemented",
|
||||
"observed_at": "2026-06-05T10:15:01Z"
|
||||
"observed_at": "2026-06-05T10:15:01Z",
|
||||
"widget_ref": "ops:backup-set:aggregate"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example: Inventory Drift
|
||||
## Example: Drift Detected
|
||||
|
||||
```json
|
||||
{
|
||||
"widgetId": "<drift-widget-uuid>",
|
||||
"eventType": "ops-inventory-drift",
|
||||
"widgetId": "<readiness-or-risk-widget-uuid>",
|
||||
"eventType": "ops-drift-detected",
|
||||
"viewContext": "ops-inventory-probe/drift",
|
||||
"metadata": {
|
||||
"type": "ops-inventory-drift",
|
||||
"type": "ops-drift-detected",
|
||||
"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",
|
||||
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-drift-detected",
|
||||
"service_id": "gitea",
|
||||
"inventory_object_id": "gitea-oci-registry",
|
||||
"drift_kind": "status_mismatch",
|
||||
@@ -197,7 +211,8 @@ Use `reason` for compact machine-readable explanations, for example:
|
||||
"observed_summary": "status_code=200",
|
||||
"observed_status": "degraded",
|
||||
"reason": "expected_status_mismatch",
|
||||
"observed_at": "2026-06-05T10:15:01Z"
|
||||
"observed_at": "2026-06-05T10:15:01Z",
|
||||
"widget_ref": "ops:readiness:gitea-registry"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,12 +223,12 @@ Use `reason` for compact machine-readable explanations, for example:
|
||||
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 `unregistered_event_type` | Event type not in Inter-Hub registry | Use a live manifest event or activate a corrected ops-hub manifest. |
|
||||
| `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`. |
|
||||
| `422` with `Widget not found` | Mapping points at a missing widget | Refresh `OPS_HUB_WIDGET_MAPPING` from authenticated widget lookup. |
|
||||
| `422` with `unregistered_policy_scope` during widget seed | Policy scope is absent | Use one of the declared ops scopes, not the old `ops-evidence` proposal. |
|
||||
|
||||
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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Ops Hub Activity-Core Widget Mapping
|
||||
|
||||
Date: 2026-06-15
|
||||
Updated: 2026-06-27
|
||||
|
||||
Workplan: `IHUB-WP-0022`
|
||||
|
||||
@@ -8,8 +9,21 @@ Workplan: `IHUB-WP-0022`
|
||||
|
||||
`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.
|
||||
Inter-Hub widget UUIDs and stable logical refs, but it must never contain
|
||||
`OPS_HUB_KEY`, bearer tokens, cookies, or any operator credential.
|
||||
|
||||
This revision aligns the contract to the live `ops-hub` manifest vocabulary
|
||||
observed on 2026-06-27 and to the source seed files in `ops-hub`:
|
||||
|
||||
- event types use the live registry names such as `ops-service-discovered` and
|
||||
`ops-drift-detected`;
|
||||
- widget types use live seed types such as `ops-service`, `ops-endpoint`,
|
||||
`ops-backup-set`, `ops-readiness-gate`, and `ops-risk`;
|
||||
- policy scopes use the declared ops scopes; there is no `ops-evidence` scope
|
||||
in the live manifest;
|
||||
- access-path evidence remains deferred until ops-hub explicitly adds an
|
||||
access-path event and widget type or activity-core maps it to a supported
|
||||
readiness/risk event by decision.
|
||||
|
||||
Activity-core currently only checks that a mapping value is present before
|
||||
returning `inter_hub_sink_deferred`. This document defines the contract that
|
||||
@@ -17,6 +31,9 @@ the future Inter-Hub submission implementation should parse.
|
||||
|
||||
## Versioned Shape
|
||||
|
||||
The shape remains version `v1`; this is a compatibility revision of the
|
||||
vocabulary inside the mapping, not a structural breaking change.
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "ops-hub.activity-core.widget-mapping.v1",
|
||||
@@ -24,87 +41,109 @@ the future Inter-Hub submission implementation should parse.
|
||||
"slug": "ops-hub",
|
||||
"id": "<ops-hub-uuid>"
|
||||
},
|
||||
"policyScope": "ops-evidence",
|
||||
"defaultViewContext": "ops-inventory-probe",
|
||||
"allowedPolicyScopes": [
|
||||
"ops-local",
|
||||
"ops-transitional-prod",
|
||||
"ops-production",
|
||||
"ops-threephoenix",
|
||||
"ops-registry",
|
||||
"ops-secrets",
|
||||
"ops-backup-retention"
|
||||
],
|
||||
"eventAliases": {
|
||||
"ops-service-observed": "ops-service-discovered",
|
||||
"ops-inventory-drift": "ops-drift-detected"
|
||||
},
|
||||
"deferredEvents": {
|
||||
"ops-access-path-checked": {
|
||||
"reason": "not_declared_by_live_ops_hub_manifest",
|
||||
"fallback": "state-hub-progress",
|
||||
"decisionNeeded": "add ops-access-path vocabulary or map access-path evidence to ops-readiness-gate-updated/ops-risk-raised"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"ops-service-observed": {
|
||||
"ops-service-discovered": {
|
||||
"family": "services",
|
||||
"aggregateWidgetRef": "ops:service:aggregate"
|
||||
"selector": "service_id",
|
||||
"defaultWidgetRef": "ops:service-catalog"
|
||||
},
|
||||
"ops-endpoint-verified": {
|
||||
"family": "endpoints",
|
||||
"aggregateWidgetRef": "ops:endpoint:aggregate"
|
||||
},
|
||||
"ops-access-path-checked": {
|
||||
"family": "accessPaths",
|
||||
"aggregateWidgetRef": "ops:access-path:aggregate"
|
||||
"selector": "<service_id>:<endpoint_id>",
|
||||
"defaultWidgetRef": "ops:endpoint:gitea-registry"
|
||||
},
|
||||
"ops-backup-verified": {
|
||||
"family": "backups",
|
||||
"aggregateWidgetRef": "ops:backup:aggregate"
|
||||
"selector": "<service_id>:<backing_store_ref>",
|
||||
"defaultWidgetRef": "ops:backup-set:aggregate",
|
||||
"requiresSeed": true
|
||||
},
|
||||
"ops-inventory-drift": {
|
||||
"family": "drift",
|
||||
"aggregateWidgetRef": "ops:drift:aggregate"
|
||||
"ops-drift-detected": {
|
||||
"family": "readiness",
|
||||
"selector": "<service_id>:<inventory_object_id>",
|
||||
"defaultWidgetRef": "ops:readiness:gitea-registry"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"aggregate": {
|
||||
"ops:service:aggregate": {
|
||||
"byRef": {
|
||||
"ops:service-catalog": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-service-card",
|
||||
"name": "Ops Service Evidence Intake"
|
||||
"widgetType": "ops-service-catalog",
|
||||
"policyScope": "ops-production",
|
||||
"name": "Operations Service Catalog"
|
||||
},
|
||||
"ops:endpoint:aggregate": {
|
||||
"ops:service:state-hub": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-endpoint-card",
|
||||
"name": "Ops Endpoint Evidence Intake"
|
||||
"widgetType": "ops-service",
|
||||
"policyScope": "ops-local",
|
||||
"name": "State Hub Service"
|
||||
},
|
||||
"ops:access-path:aggregate": {
|
||||
"ops:service:gitea": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-access-path-card",
|
||||
"name": "Ops Access Path Evidence Intake"
|
||||
"widgetType": "ops-service",
|
||||
"policyScope": "ops-transitional-prod",
|
||||
"name": "Gitea Service"
|
||||
},
|
||||
"ops:backup:aggregate": {
|
||||
"ops:service:inter-hub": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-backup-card",
|
||||
"name": "Ops Backup Evidence Intake"
|
||||
"widgetType": "ops-service",
|
||||
"policyScope": "ops-production",
|
||||
"name": "Inter-Hub Service"
|
||||
},
|
||||
"ops:drift:aggregate": {
|
||||
"ops:endpoint:gitea-registry": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-drift-card",
|
||||
"name": "Ops Inventory Drift Evidence Intake"
|
||||
"widgetType": "ops-endpoint",
|
||||
"policyScope": "ops-registry",
|
||||
"name": "Gitea Registry Endpoint"
|
||||
},
|
||||
"ops:readiness:gitea-registry": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-readiness-gate",
|
||||
"policyScope": "ops-registry",
|
||||
"name": "Gitea Registry Readiness"
|
||||
},
|
||||
"ops:backup-set:aggregate": {
|
||||
"widgetId": "<uuid>",
|
||||
"widgetType": "ops-backup-set",
|
||||
"policyScope": "ops-backup-retention",
|
||||
"name": "Ops Backup Evidence Intake",
|
||||
"requiresSeed": true
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"state-hub": {
|
||||
"widgetRef": "ops:service:state-hub",
|
||||
"widgetId": "<uuid>"
|
||||
}
|
||||
"state-hub": { "widgetRef": "ops:service:state-hub" },
|
||||
"gitea": { "widgetRef": "ops:service:gitea" },
|
||||
"inter-hub": { "widgetRef": "ops:service:inter-hub" }
|
||||
},
|
||||
"endpoints": {
|
||||
"gitea:gitea-oci-registry": {
|
||||
"widgetRef": "ops:endpoint:gitea-registry",
|
||||
"widgetId": "<uuid>"
|
||||
}
|
||||
},
|
||||
"accessPaths": {
|
||||
"gitea:gitea-access-1": {
|
||||
"widgetRef": "ops:access-path:gitea-access-1",
|
||||
"widgetId": "<uuid>"
|
||||
}
|
||||
"gitea:gitea-oci-registry": { "widgetRef": "ops:endpoint:gitea-registry" }
|
||||
},
|
||||
"backups": {
|
||||
"gitea:database:gitea-db": {
|
||||
"widgetRef": "ops:backup:gitea-db",
|
||||
"widgetId": "<uuid>"
|
||||
}
|
||||
"gitea:database:gitea-db": { "widgetRef": "ops:backup-set:aggregate" }
|
||||
},
|
||||
"drift": {
|
||||
"gitea:gitea-oci-registry": {
|
||||
"widgetRef": "ops:drift:gitea-oci-registry",
|
||||
"widgetId": "<uuid>"
|
||||
}
|
||||
"readiness": {
|
||||
"gitea:gitea-oci-registry": { "widgetRef": "ops:readiness:gitea-registry" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,44 +154,60 @@ the future Inter-Hub submission implementation should parse.
|
||||
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["<service_id>"]`.
|
||||
3. For `ops-endpoint-verified`, use
|
||||
`endpoints["<service_id>:<endpoint_id>"]`.
|
||||
4. For `ops-access-path-checked`, use
|
||||
`accessPaths["<service_id>:<access_path_id>"]`.
|
||||
`widgets.byRef`, use it.
|
||||
2. If the event name is present in `eventAliases`, translate it to the live
|
||||
event name before selector lookup and submission.
|
||||
3. For `ops-service-discovered`, use `services["<service_id>"]`; otherwise use
|
||||
`ops:service-catalog`.
|
||||
4. For `ops-endpoint-verified`, use
|
||||
`endpoints["<service_id>:<endpoint_id>"]`; otherwise skip or use a
|
||||
deliberately seeded endpoint aggregate.
|
||||
5. For `ops-backup-verified`, use
|
||||
`backups["<service_id>:<backing_store_ref>"]`.
|
||||
6. For `ops-inventory-drift`, use
|
||||
`drift["<service_id>:<inventory_object_id>"]`.
|
||||
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.
|
||||
`backups["<service_id>:<backing_store_ref>"]`; otherwise skip until a
|
||||
backup-set widget is seeded.
|
||||
6. For `ops-drift-detected`, use
|
||||
`readiness["<service_id>:<inventory_object_id>"]`; otherwise use a seeded
|
||||
readiness/risk widget chosen by the operator.
|
||||
7. For `ops-access-path-checked`, do not submit to Inter-Hub yet. Keep State
|
||||
Hub fallback evidence unless ops-hub adds access-path vocabulary or an
|
||||
explicit decision maps access-path checks to readiness/risk events.
|
||||
8. If no selector can resolve to a widget id, skip Inter-Hub submission with a
|
||||
non-secret result naming the missing selector.
|
||||
|
||||
## Bootstrap Widget Names
|
||||
|
||||
The initial aggregate widgets should be seeded before activity-core is pointed
|
||||
at Inter-Hub:
|
||||
These refs are already present in the current `ops-hub` seed file and should be
|
||||
looked up in the protected widget registry during the authenticated bootstrap:
|
||||
|
||||
| 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 |
|
||||
| Widget ref | Widget type | Name |
|
||||
| --- | --- | --- |
|
||||
| `ops:service-catalog` | `ops-service-catalog` | Operations Service Catalog |
|
||||
| `ops:service:gitea` | `ops-service` | Gitea Service |
|
||||
| `ops:service:state-hub` | `ops-service` | State Hub Service |
|
||||
| `ops:service:inter-hub` | `ops-service` | Inter-Hub Service |
|
||||
| `ops:endpoint:gitea-registry` | `ops-endpoint` | Gitea Registry Endpoint |
|
||||
| `ops:readiness:gitea-registry` | `ops-readiness-gate` | Gitea Registry Readiness |
|
||||
|
||||
Additional seed required before backup evidence can be smoke-tested:
|
||||
|
||||
| Widget ref | Widget type | Suggested name | Policy scope |
|
||||
| --- | --- | --- | --- |
|
||||
| `ops:backup-set:aggregate` | `ops-backup-set` | Ops Backup Evidence Intake | `ops-backup-retention` |
|
||||
|
||||
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.
|
||||
When a per-entity widget is added, update the mapping and keep a deliberate
|
||||
fallback only when the fallback widget actually exists in the protected widget
|
||||
registry.
|
||||
|
||||
## 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.
|
||||
- Event aliases are a transition aid only. New activity-core submissions should
|
||||
use live registry event names directly.
|
||||
- Removing a widget mapping is a breaking change for that selector unless a
|
||||
verified fallback widget 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
|
||||
@@ -160,7 +215,9 @@ widget as the fallback.
|
||||
|
||||
## Minimum Valid Mapping
|
||||
|
||||
For the first live smoke, an aggregate-only mapping is enough:
|
||||
For the first live smoke, use only live registry events and widgets that are
|
||||
known from the seed contract and can be confirmed through an authenticated
|
||||
widget lookup. Backup and access-path evidence can remain deferred.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -169,37 +226,30 @@ For the first live smoke, an aggregate-only mapping is enough:
|
||||
"slug": "ops-hub",
|
||||
"id": "<ops-hub-uuid>"
|
||||
},
|
||||
"policyScope": "ops-evidence",
|
||||
"defaultViewContext": "ops-inventory-probe",
|
||||
"eventAliases": {
|
||||
"ops-service-observed": "ops-service-discovered",
|
||||
"ops-inventory-drift": "ops-drift-detected"
|
||||
},
|
||||
"events": {
|
||||
"ops-service-observed": {
|
||||
"ops-service-discovered": {
|
||||
"family": "services",
|
||||
"aggregateWidgetRef": "ops:service:aggregate"
|
||||
"defaultWidgetRef": "ops:service-catalog"
|
||||
},
|
||||
"ops-endpoint-verified": {
|
||||
"family": "endpoints",
|
||||
"aggregateWidgetRef": "ops:endpoint:aggregate"
|
||||
"defaultWidgetRef": "ops:endpoint:gitea-registry"
|
||||
},
|
||||
"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"
|
||||
"ops-drift-detected": {
|
||||
"family": "readiness",
|
||||
"defaultWidgetRef": "ops:readiness:gitea-registry"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"aggregate": {
|
||||
"ops:service:aggregate": { "widgetId": "<uuid>" },
|
||||
"ops:endpoint:aggregate": { "widgetId": "<uuid>" },
|
||||
"ops:access-path:aggregate": { "widgetId": "<uuid>" },
|
||||
"ops:backup:aggregate": { "widgetId": "<uuid>" },
|
||||
"ops:drift:aggregate": { "widgetId": "<uuid>" }
|
||||
"byRef": {
|
||||
"ops:service-catalog": { "widgetId": "<uuid>" },
|
||||
"ops:endpoint:gitea-registry": { "widgetId": "<uuid>" },
|
||||
"ops:readiness:gitea-registry": { "widgetId": "<uuid>" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Ops Hub Evidence Intake - Current State
|
||||
|
||||
Date: 2026-06-15
|
||||
Updated: 2026-06-27
|
||||
|
||||
Workplan: `IHUB-WP-0022`
|
||||
|
||||
@@ -17,6 +18,26 @@ slice is therefore contract-first:
|
||||
- wait on live ops-hub manifest/widgets, key provisioning, and production
|
||||
smoke before enabling per-entity Inter-Hub submission.
|
||||
|
||||
|
||||
## 2026-06-27 Live Vocabulary Alignment
|
||||
|
||||
Public production probes now show an `ops-hub` row and public registry
|
||||
vocabulary on `https://hub.coulomb.social`. The live ops-hub seed vocabulary
|
||||
supersedes the early activity-core proposal for several names:
|
||||
|
||||
| Early proposal | Live target |
|
||||
| --- | --- |
|
||||
| `ops-service-observed` | `ops-service-discovered` |
|
||||
| `ops-inventory-drift` | `ops-drift-detected` |
|
||||
| `ops-access-path-checked` | Deferred; no live access-path event or widget type |
|
||||
| `ops-evidence` policy scope | Use declared scopes such as `ops-production`, `ops-registry`, or `ops-backup-retention` |
|
||||
| card-style widget types | Use live widget types such as `ops-service`, `ops-endpoint`, `ops-backup-set`, `ops-readiness-gate`, and `ops-risk` |
|
||||
|
||||
The contract docs have been revised to target the live seed vocabulary and to
|
||||
keep access-path evidence on the State Hub fallback path until ops-hub either
|
||||
adds access-path vocabulary or records an explicit readiness/risk mapping
|
||||
decision.
|
||||
|
||||
## Inter-Hub API Surface
|
||||
|
||||
The current repo supports the necessary primitives through `/api/v2`.
|
||||
@@ -119,30 +140,39 @@ Known gates before per-entity Inter-Hub submission can be treated as live:
|
||||
|
||||
## Recommended Manifest Vocabulary
|
||||
|
||||
Use one policy scope for the first slice:
|
||||
Use the live ops-hub manifest vocabulary rather than the early proposal names.
|
||||
The current target is:
|
||||
|
||||
- `ops-evidence`
|
||||
Policy scopes:
|
||||
|
||||
Use one annotation category:
|
||||
- `ops-local`
|
||||
- `ops-transitional-prod`
|
||||
- `ops-production`
|
||||
- `ops-threephoenix`
|
||||
- `ops-registry`
|
||||
- `ops-secrets`
|
||||
- `ops-backup-retention`
|
||||
|
||||
- `ops-risk`
|
||||
Widget types for activity-core evidence:
|
||||
|
||||
Use these widget types unless the operator prefers to keep a smaller aggregate
|
||||
surface:
|
||||
- `ops-service-catalog`
|
||||
- `ops-service`
|
||||
- `ops-endpoint`
|
||||
- `ops-backup-set`
|
||||
- `ops-readiness-gate`
|
||||
- `ops-risk` when risk-specific drift representation is needed
|
||||
|
||||
- `ops-service-card`
|
||||
- `ops-endpoint-card`
|
||||
- `ops-access-path-card`
|
||||
- `ops-backup-card`
|
||||
- `ops-drift-card`
|
||||
Event types for the first Inter-Hub activity-core slice:
|
||||
|
||||
Use the activity-core event types exactly as published:
|
||||
|
||||
- `ops-service-observed`
|
||||
- `ops-service-discovered`
|
||||
- `ops-endpoint-verified`
|
||||
- `ops-access-path-checked`
|
||||
- `ops-backup-verified`
|
||||
- `ops-inventory-drift`
|
||||
- `ops-backup-verified` after a backup-set widget exists
|
||||
- `ops-drift-detected`
|
||||
|
||||
Keep `ops-access-path-checked` deferred until ops-hub declares access-path
|
||||
vocabulary or a separate decision maps those checks to readiness/risk events.
|
||||
Do not seed the old `ops-evidence` policy scope unless the manifest is
|
||||
explicitly expanded to include it.
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ status: active
|
||||
owner: codex
|
||||
topic_slug: inter_hub
|
||||
created: "2026-06-15"
|
||||
updated: "2026-06-16"
|
||||
updated: "2026-06-27"
|
||||
planning_priority: high
|
||||
planning_order: 22
|
||||
related_repos:
|
||||
@@ -94,20 +94,22 @@ closed.
|
||||
|
||||
## Proposed Evidence Vocabulary
|
||||
|
||||
Activity-core has already declared the event contracts it wants to send:
|
||||
The original activity-core suggestion used five evidence names. Production
|
||||
ops-hub now exposes a live seed vocabulary, so the Inter-Hub activation target
|
||||
is the compatibility mapping below rather than the early names verbatim:
|
||||
|
||||
| Event type | Suggested widget family | Purpose |
|
||||
|---|---|---|
|
||||
| `ops-service-observed` | service inventory | Record that a service exists and was observed. |
|
||||
| `ops-endpoint-verified` | endpoint inventory | Record endpoint reachability, auth challenge, or health verification. |
|
||||
| `ops-access-path-checked` | access path inventory | Record operator or service access path verification. |
|
||||
| `ops-backup-verified` | backup inventory | Record backup presence, recency, or restore-drill evidence. |
|
||||
| `ops-inventory-drift` | drift inventory | Record drift between expected and observed operations inventory. |
|
||||
| Original evidence intent | Live Inter-Hub target | Widget family | Purpose |
|
||||
|---|---|---|---|
|
||||
| `ops-service-observed` | `ops-service-discovered` | `ops-service` / `ops-service-catalog` | Record that a service exists and was discovered. |
|
||||
| `ops-endpoint-verified` | `ops-endpoint-verified` | `ops-endpoint` | Record endpoint reachability, auth challenge, or health verification. |
|
||||
| `ops-access-path-checked` | deferred | State Hub fallback until ops-hub adds access-path vocabulary or maps it to readiness/risk | Record operator or service access path verification without inventing unsupported registry names. |
|
||||
| `ops-backup-verified` | `ops-backup-verified` | `ops-backup-set` | Record backup presence, recency, or restore-drill evidence after a backup-set widget exists. |
|
||||
| `ops-inventory-drift` | `ops-drift-detected` | `ops-readiness-gate` / `ops-risk` | Record drift between expected and observed operations inventory. |
|
||||
|
||||
The first implementation should keep one stable widget per entity and evidence
|
||||
family where possible. If activity-core cannot know entity identity reliably,
|
||||
use one aggregate intake widget per family as a conservative first slice, then
|
||||
split into per-entity widgets after payload evidence proves stable.
|
||||
The first implementation should use existing seeded widgets where possible and
|
||||
seed only the missing backup/risk widgets that are needed for the attended
|
||||
smoke. If activity-core cannot know entity identity reliably, keep State Hub
|
||||
fallback evidence rather than submitting to a made-up aggregate widget.
|
||||
|
||||
## Tasks
|
||||
|
||||
@@ -179,6 +181,13 @@ 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`.
|
||||
|
||||
Compatibility note (2026-06-27): revised the mapping contract to match the
|
||||
live ops-hub seed vocabulary. The mapping now aliases `ops-service-observed`
|
||||
to `ops-service-discovered`, aliases `ops-inventory-drift` to
|
||||
`ops-drift-detected`, uses declared ops policy scopes instead of the old
|
||||
`ops-evidence` proposal, and defers access-path evidence until ops-hub has a
|
||||
supported event/widget target.
|
||||
|
||||
---
|
||||
|
||||
### T03 - Prepare manifest vocabulary and seed widgets
|
||||
@@ -218,6 +227,12 @@ 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.
|
||||
|
||||
Progress note (2026-06-27): public production probes show an `ops-hub` row and
|
||||
the live seed registry vocabulary, and the contract docs now target that live
|
||||
vocabulary. T03 remains waiting because protected widget lookup, widget ids,
|
||||
any missing backup/risk seed widgets, and authenticated smoke evidence still
|
||||
require the operator/runtime key path.
|
||||
|
||||
---
|
||||
|
||||
### T04 - Provision the runtime API key outside Git
|
||||
@@ -289,6 +304,11 @@ Inter-Hub request envelope, shared validation rules, idempotency expectations,
|
||||
forbidden payload material, expected API errors, and one example for each
|
||||
activity-core event type.
|
||||
|
||||
Compatibility note (2026-06-27): revised payload examples to submit only live
|
||||
ops-hub event types. Access-path payloads are documented as deferred fallback
|
||||
evidence, and old event names are treated as aliases that should not be posted
|
||||
to Inter-Hub.
|
||||
|
||||
---
|
||||
|
||||
### T06 - Validate fallback-first intake
|
||||
|
||||
Reference in New Issue
Block a user