@opensoha/contracts 0.1.27 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/COMPATIBILITY.md +4 -3
  2. package/README.md +8 -9
  3. package/compat/consumer-revisions.json +1 -1
  4. package/compat/json-schema-ecosystem-0.1.0.json +1 -1
  5. package/compat/openapi-loose-boundaries-0.1.0.json +1 -1
  6. package/fixtures/openapi/KubernetesClusterRegistrationRequest/invalid/metric-cluster-value-length.json +3 -0
  7. package/fixtures/openapi/KubernetesClusterRegistrationRequest/invalid/metric-cluster-value-type.json +3 -0
  8. package/fixtures/openapi/KubernetesClusterRegistrationRequest/valid/metric-cluster-alias.json +5 -0
  9. package/fixtures/openapi/KubernetesClusterRegistrationRequest/valid/metric-cluster-reset.json +3 -0
  10. package/fixtures/openapi/KubernetesHelmReleaseValuesUpdateInput/invalid/revision-overflow.json +4 -0
  11. package/fixtures/openapi/KubernetesHelmReleaseValuesUpdateInput/valid/legacy.json +3 -0
  12. package/fixtures/openapi/KubernetesHelmReleaseValuesUpdateInput/valid/observed-revision.json +4 -0
  13. package/fixtures/openapi/KubernetesJobDetail/invalid/execution-observation.json +23 -0
  14. package/fixtures/openapi/KubernetesJobDetail/valid/execution-observation.json +23 -0
  15. package/fixtures/openapi/KubernetesNode/invalid/labels.json +10 -0
  16. package/fixtures/openapi/KubernetesNode/valid/labels.json +11 -0
  17. package/fixtures/openapi/KubernetesPodListEnvelope/invalid/empty-uid.json +13 -0
  18. package/fixtures/openapi/KubernetesPodListEnvelope/invalid/invalid-container-state.json +20 -0
  19. package/fixtures/openapi/KubernetesPodListEnvelope/invalid/missing-container-ready.json +19 -0
  20. package/fixtures/openapi/KubernetesPodListEnvelope/valid/container-statuses.json +67 -0
  21. package/fixtures/openapi/KubernetesPodListEnvelope/valid/running-pod.json +1 -0
  22. package/fixtures/openapi/KubernetesPrometheusDetail/invalid/metric-cluster-value-type.json +4 -0
  23. package/fixtures/openapi/KubernetesPrometheusDetail/valid/metric-cluster-alias.json +5 -0
  24. package/fixtures/openapi/KubernetesResourceGraphEnvelope/invalid/network-edge-source-payload.json +73 -0
  25. package/fixtures/openapi/KubernetesResourceGraphEnvelope/valid/network-edge-facts.json +67 -0
  26. package/fixtures/openapi/KubernetesSecretCertificate/invalid/secret-data.json +1 -0
  27. package/fixtures/openapi/KubernetesSecretCertificate/invalid/status.json +1 -0
  28. package/fixtures/openapi/KubernetesSecretCertificate/valid/available.json +1 -0
  29. package/fixtures/openapi/KubernetesSecretCertificate/valid/missing.json +1 -0
  30. package/gen/go/sohaapi/client.go +1 -1
  31. package/gen/go/sohaapi/client_test.go +13 -0
  32. package/gen/go/sohaapi/kubernetes_workload_optional_test.go +40 -0
  33. package/gen/go/sohaapi/pod_status_compat_test.go +36 -0
  34. package/gen/go/sohaapi/types.go +421 -88
  35. package/gen/ts/sohaapi/index.ts +756 -4
  36. package/helmrelease/runtime/values_upgrade.go +78 -0
  37. package/helmrelease/runtime/values_upgrade_test.go +123 -0
  38. package/helmrelease/values_update.go +31 -0
  39. package/openapi/soha-api.json +1191 -14
  40. package/openapi/soha-api.yaml +588 -28
  41. package/openapi/spec_generated.go +3 -3
  42. package/package.json +1 -1
  43. package/resource/agent_profiles.go +9 -15
  44. package/resource/agent_profiles_test.go +9 -3
  45. package/resource/custom_graph_page.go +18 -0
  46. package/resource/models.go +102 -57
  47. package/resource/network_topology.go +22 -0
  48. package/resource/network_topology_projection.go +36 -22
  49. package/resource/network_topology_projection_test.go +4 -0
  50. package/resource/network_topology_test.go +20 -0
  51. package/resource/network_topology_validation.go +5 -0
  52. package/resource/observed_workload.go +18 -0
  53. package/resource/pod_status_test.go +22 -0
  54. package/resource/rbac_graph.go +4 -0
  55. package/resource/rollout_optional_test.go +33 -0
  56. package/resource/runtime/network_relationship_primitives.go +66 -0
  57. package/resource/runtime/network_relationship_primitives_test.go +147 -0
  58. package/resource/runtime/network_topology.go +17 -5
  59. package/resource/runtime/network_topology_builder.go +405 -0
  60. package/resource/runtime/network_topology_builder_backends.go +195 -0
  61. package/resource/runtime/network_topology_builder_gateway_l4_test.go +86 -0
  62. package/resource/runtime/network_topology_builder_ingress.go +302 -0
  63. package/resource/runtime/network_topology_builder_legacy.go +66 -0
  64. package/resource/runtime/network_topology_builder_providers.go +242 -0
  65. package/resource/runtime/network_topology_builder_test.go +186 -0
  66. package/resource/runtime/network_topology_declarations.go +329 -0
  67. package/resource/runtime/resource_edit.go +22 -0
  68. package/resource/runtime/resource_edit_test.go +32 -0
  69. package/resource/runtime/resource_graph_collect.go +325 -0
  70. package/resource/runtime/resource_graph_configuration.go +59 -0
  71. package/resource/runtime/resource_graph_configuration_collect.go +198 -0
  72. package/resource/runtime/resource_graph_configuration_test.go +470 -0
  73. package/resource/runtime/resource_graph_custom_page.go +159 -0
  74. package/resource/runtime/resource_graph_custom_root.go +132 -0
  75. package/resource/runtime/resource_graph_custom_root_test.go +197 -0
  76. package/resource/runtime/resource_graph_enrichment.go +417 -0
  77. package/resource/runtime/resource_graph_enrichment_hpa_test.go +117 -0
  78. package/resource/runtime/resource_graph_enrichment_test.go +201 -0
  79. package/resource/runtime/resource_graph_metadata.go +125 -0
  80. package/resource/runtime/resource_graph_metadata_test.go +204 -0
  81. package/resource/runtime/resource_graph_network.go +224 -0
  82. package/resource/runtime/resource_graph_network_collect.go +174 -0
  83. package/resource/runtime/resource_graph_network_collect_test.go +147 -0
  84. package/resource/runtime/resource_graph_network_test.go +342 -0
  85. package/resource/runtime/resource_graph_rbac.go +278 -0
  86. package/resource/runtime/resource_graph_rbac_test.go +372 -0
  87. package/resource/runtime/resource_graph_rules.go +834 -0
  88. package/resource/runtime/resource_graph_storage.go +144 -0
  89. package/resource/runtime/resource_graph_storage_collect.go +163 -0
  90. package/resource/runtime/resource_graph_storage_test.go +397 -0
  91. package/resource/runtime/resource_graph_test.go +627 -0
  92. package/resource/runtime/secret_certificate.go +44 -0
  93. package/resource/runtime/secret_certificate_test.go +87 -0
  94. package/resource/storage_graph.go +4 -0
package/COMPATIBILITY.md CHANGED
@@ -62,9 +62,10 @@ Release order for `0.1.x`:
62
62
  3. Run `npm test`, `npm pack --dry-run`, `npm run release:artifacts`, and
63
63
  the consumer matrix.
64
64
  4. Create tag `v<version>`.
65
- 5. Let the release workflow publish npm when `NPM_TOKEN` is configured and
66
- create the GitHub release with the npm tarball, `.sha256` checksum, and
67
- release manifest attached.
65
+ 5. Let the release workflow stage the npm package with `NPM_TOKEN`. After a
66
+ maintainer approves the stage with 2FA, dispatch recovery for the same tag.
67
+ The workflow verifies the public npm package and Go tag before attaching
68
+ the tarball, checksums, and release manifest to the GitHub release.
68
69
 
69
70
  ## Compatibility Baseline
70
71
 
package/README.md CHANGED
@@ -232,17 +232,16 @@ the tarball checksum, manifest metadata, packed `package.json`, every exported
232
232
  package entrypoint, both OpenAPI artifact hashes, the SBOM identity and
233
233
  checksum, the provenance policy and checksum, and the consumer matrix summary.
234
234
 
235
- The tag workflow runs `npm test`, `npm pack --dry-run`, the strict consumer
236
- matrix, requires `NPM_TOKEN`, publishes npm with provenance, verifies the npm
237
- package and Go module tag after publish, and attaches the tarball, checksum,
238
- SBOM, SBOM checksum, provenance policy, provenance policy checksum, manifest,
239
- and consumer matrix to the GitHub release. It then downloads the published
240
- release assets and runs `release:verify` against the downloaded tarball.
235
+ The tag workflow runs `npm test`, `npm pack --dry-run`, and the strict consumer
236
+ matrix, then uses `NPM_TOKEN` to stage the npm tarball with provenance. A
237
+ maintainer approves the stage on npmjs.com with 2FA. Dispatch the release
238
+ workflow with the same tag and `publish_mode=recover` after approval. Recovery
239
+ verifies the public npm package and Go module tag, attaches the release
240
+ artifacts to GitHub, and verifies the downloaded tarball.
241
241
 
242
242
  The local release manifest records the provenance policy expected from the tag
243
- workflow. It does not prove a remote npm registry attestation by itself; that
244
- evidence is produced and checked only after the tag workflow publishes with
245
- `npm publish --provenance`.
243
+ workflow. Verify the registry attestation's artifact digest and source revision
244
+ after approval makes the package public.
246
245
 
247
246
  During private staging, consumers intentionally use sibling repository
248
247
  checkouts, `go.work` / `replace` entries, and `file:../soha-contracts` package
@@ -1,5 +1,5 @@
1
1
  {
2
- "soha": "57bfa94873ff2825bccc6312842859011c1ec23a",
2
+ "soha": "59e929046927eecf1977bc50a446fd5d823d1d31",
3
3
  "soha-web": "447cbe5358802ef40d86ac67b7b59c5afe9c846a",
4
4
  "soha-app": "0c3c76c373246f7d2218b48f68b89bf3097f777b",
5
5
  "soha-cli": "1c9b760c986a9c1a5148db55b72ab99d23edd7a3",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.27",
2
+ "version": "0.1.29",
3
3
  "schemas": {
4
4
  "plugins/plugin-manifest.schema.json": {
5
5
  "rootRequired": ["id", "name", "publisher", "type", "version"],
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.27",
2
+ "version": "0.1.29",
3
3
  "generatedFrom": "openapi/soha-api.yaml",
4
4
  "entries": [
5
5
  {
@@ -0,0 +1,3 @@
1
+ {
2
+ "prometheusClusterLabelValue": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "example",
3
+ "prometheusClusterLabel": "cluster",
4
+ "prometheusClusterLabelValue": "middleware-prod"
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "prometheusClusterLabelValue": ""
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "content": "{}\n",
3
+ "expectedRevision": 2147483647
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "content": "replicaCount: 2\n",
3
+ "expectedRevision": 4
4
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "task117-demo-failed",
3
+ "namespace": "task117-jobs-demo",
4
+ "completions": 1,
5
+ "completionTarget": 1,
6
+ "parallelism": 1,
7
+ "succeeded": 0,
8
+ "failed": 1,
9
+ "active": 0,
10
+ "suspend": false,
11
+ "backoffLimit": -1,
12
+ "activeDeadlineSeconds": 3600,
13
+ "ttlSecondsAfterFinished": 604800,
14
+ "startTime": "2026-11-01T02:00:00Z",
15
+ "conditions": [
16
+ {
17
+ "type": "Failed",
18
+ "status": "True",
19
+ "reason": "BackoffLimitExceeded",
20
+ "lastTransitionTime": "2026-11-01T02:00:20Z"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "task117-demo-failed",
3
+ "namespace": "task117-jobs-demo",
4
+ "completions": 1,
5
+ "completionTarget": 1,
6
+ "parallelism": 1,
7
+ "succeeded": 0,
8
+ "failed": 1,
9
+ "active": 0,
10
+ "suspend": false,
11
+ "backoffLimit": 0,
12
+ "activeDeadlineSeconds": 3600,
13
+ "ttlSecondsAfterFinished": 604800,
14
+ "startTime": "2026-11-01T02:00:00Z",
15
+ "conditions": [
16
+ {
17
+ "type": "Failed",
18
+ "status": "True",
19
+ "reason": "BackoffLimitExceeded",
20
+ "lastTransitionTime": "2026-11-01T02:00:20Z"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "worker-a",
3
+ "status": "Ready",
4
+ "unschedulable": false,
5
+ "podCount": 0,
6
+ "ageSeconds": 0,
7
+ "labels": {
8
+ "pool": 1
9
+ }
10
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "worker-a",
3
+ "status": "Ready",
4
+ "unschedulable": false,
5
+ "podCount": 0,
6
+ "ageSeconds": 0,
7
+ "labels": {
8
+ "node-role.kubernetes.io/worker": "",
9
+ "pool": "gpu"
10
+ }
11
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "name": "api",
5
+ "namespace": "apps",
6
+ "uid": "",
7
+ "phase": "Running",
8
+ "readyContainers": "1/1",
9
+ "restarts": 0,
10
+ "ageSeconds": 60
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "name": "api",
5
+ "namespace": "apps",
6
+ "phase": "Running",
7
+ "readyContainers": "1/1",
8
+ "restarts": 0,
9
+ "ageSeconds": 60,
10
+ "containerStatuses": [
11
+ {
12
+ "name": "api",
13
+ "ready": true,
14
+ "state": "healthy",
15
+ "kind": "container"
16
+ }
17
+ ]
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "name": "api",
5
+ "namespace": "apps",
6
+ "phase": "Pending",
7
+ "readyContainers": "0/1",
8
+ "restarts": 0,
9
+ "ageSeconds": 60,
10
+ "containerStatuses": [
11
+ {
12
+ "name": "api",
13
+ "state": "unknown",
14
+ "kind": "container"
15
+ }
16
+ ]
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "name": "api",
5
+ "namespace": "apps",
6
+ "phase": "Running",
7
+ "readyContainers": "1/3",
8
+ "restarts": 2,
9
+ "ageSeconds": 60,
10
+ "qosClass": "Burstable",
11
+ "containerStatuses": [
12
+ {
13
+ "name": "migrate",
14
+ "kind": "init",
15
+ "ready": false,
16
+ "state": "terminated",
17
+ "reason": "Completed",
18
+ "exitCode": 0
19
+ },
20
+ {
21
+ "name": "proxy-init",
22
+ "kind": "sidecar",
23
+ "ready": true,
24
+ "state": "running"
25
+ },
26
+ {
27
+ "name": "api",
28
+ "ready": true,
29
+ "state": "running",
30
+ "kind": "container"
31
+ },
32
+ {
33
+ "name": "proxy",
34
+ "ready": false,
35
+ "state": "waiting",
36
+ "reason": "CrashLoopBackOff",
37
+ "kind": "container"
38
+ },
39
+ {
40
+ "name": "worker",
41
+ "ready": false,
42
+ "state": "unknown",
43
+ "kind": "container"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "name": "job",
49
+ "namespace": "apps",
50
+ "phase": "Succeeded",
51
+ "readyContainers": "0/1",
52
+ "restarts": 0,
53
+ "ageSeconds": 30,
54
+ "qosClass": "BestEffort",
55
+ "containerStatuses": [
56
+ {
57
+ "name": "job",
58
+ "ready": false,
59
+ "state": "terminated",
60
+ "reason": "Completed",
61
+ "exitCode": 0,
62
+ "kind": "container"
63
+ }
64
+ ]
65
+ }
66
+ ]
67
+ }
@@ -3,6 +3,7 @@
3
3
  {
4
4
  "name": "api-7c8d9f",
5
5
  "namespace": "production",
6
+ "uid": "97cb3639-ff9d-4633-a70b-70c8149d6f4a",
6
7
  "phase": "Running",
7
8
  "nodeName": "worker-1",
8
9
  "podIp": "10.42.1.8",
@@ -0,0 +1,4 @@
1
+ {
2
+ "hasBearerToken": false,
3
+ "clusterLabelValue": 7
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "hasBearerToken": false,
3
+ "clusterLabel": "cluster",
4
+ "clusterLabelValue": "middleware-prod"
5
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "data": {
3
+ "clusterId": "cluster-a",
4
+ "namespace": "team-a",
5
+ "generatedAt": "2026-08-23T08:00:00Z",
6
+ "rootId": "apps/v1:Deployment:team-a/api",
7
+ "nodes": [
8
+ {
9
+ "id": "apps/v1:Deployment:team-a/api",
10
+ "resource": {
11
+ "clusterId": "cluster-a",
12
+ "apiVersion": "apps/v1",
13
+ "kind": "Deployment",
14
+ "name": "api",
15
+ "namespace": "team-a",
16
+ "scopeMode": "namespace",
17
+ "data": {
18
+ "privateKey": "forbidden"
19
+ }
20
+ },
21
+ "status": "healthy"
22
+ },
23
+ {
24
+ "id": "v1:Pod:team-a/api-1",
25
+ "resource": {
26
+ "clusterId": "cluster-a",
27
+ "apiVersion": "v1",
28
+ "kind": "Pod",
29
+ "name": "api-1",
30
+ "namespace": "team-a",
31
+ "scopeMode": "namespace"
32
+ },
33
+ "status": "Running"
34
+ }
35
+ ],
36
+ "edges": [
37
+ {
38
+ "id": "owns:deployment-api:pod-api-1",
39
+ "sourceId": "apps/v1:Deployment:team-a/api",
40
+ "targetId": "v1:Pod:team-a/api-1",
41
+ "relation": "owns",
42
+ "resolutionState": "resolved",
43
+ "protocol": "HTTPS",
44
+ "port": "443",
45
+ "sectionName": "https",
46
+ "fieldPath": "spec.parentRefs[0]",
47
+ "note": "Declared listener attachment; controller execution unverified",
48
+ "sourceRef": {
49
+ "clusterId": "cluster-a",
50
+ "apiVersion": "apps/v1",
51
+ "kind": "Deployment",
52
+ "name": "api",
53
+ "namespace": "team-a",
54
+ "scopeMode": "namespace",
55
+ "data": {
56
+ "privateKey": "forbidden"
57
+ }
58
+ }
59
+ }
60
+ ],
61
+ "evidence": [
62
+ {
63
+ "id": "event/team-a/api-1-scheduled",
64
+ "type": "kubernetes-event",
65
+ "severity": "info",
66
+ "summary": "Pod scheduled",
67
+ "observedAt": "2026-08-23T07:59:58Z",
68
+ "resourceId": "v1:Pod:team-a/api-1"
69
+ }
70
+ ],
71
+ "warnings": []
72
+ }
73
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "data": {
3
+ "clusterId": "cluster-a",
4
+ "namespace": "team-a",
5
+ "generatedAt": "2026-08-23T08:00:00Z",
6
+ "rootId": "apps/v1:Deployment:team-a/api",
7
+ "nodes": [
8
+ {
9
+ "id": "apps/v1:Deployment:team-a/api",
10
+ "resource": {
11
+ "clusterId": "cluster-a",
12
+ "apiVersion": "apps/v1",
13
+ "kind": "Deployment",
14
+ "name": "api",
15
+ "namespace": "team-a",
16
+ "scopeMode": "namespace"
17
+ },
18
+ "status": "healthy"
19
+ },
20
+ {
21
+ "id": "v1:Pod:team-a/api-1",
22
+ "resource": {
23
+ "clusterId": "cluster-a",
24
+ "apiVersion": "v1",
25
+ "kind": "Pod",
26
+ "name": "api-1",
27
+ "namespace": "team-a",
28
+ "scopeMode": "namespace"
29
+ },
30
+ "status": "Running"
31
+ }
32
+ ],
33
+ "edges": [
34
+ {
35
+ "id": "owns:deployment-api:pod-api-1",
36
+ "sourceId": "apps/v1:Deployment:team-a/api",
37
+ "targetId": "v1:Pod:team-a/api-1",
38
+ "relation": "owns",
39
+ "resolutionState": "resolved",
40
+ "protocol": "HTTPS",
41
+ "port": "443",
42
+ "sectionName": "https",
43
+ "fieldPath": "spec.parentRefs[0]",
44
+ "note": "Declared listener attachment; controller execution unverified",
45
+ "sourceRef": {
46
+ "clusterId": "cluster-a",
47
+ "apiVersion": "apps/v1",
48
+ "kind": "Deployment",
49
+ "name": "api",
50
+ "namespace": "team-a",
51
+ "scopeMode": "namespace"
52
+ }
53
+ }
54
+ ],
55
+ "evidence": [
56
+ {
57
+ "id": "event/team-a/api-1-scheduled",
58
+ "type": "kubernetes-event",
59
+ "severity": "info",
60
+ "summary": "Pod scheduled",
61
+ "observedAt": "2026-08-23T07:59:58Z",
62
+ "resourceId": "v1:Pod:team-a/api-1"
63
+ }
64
+ ],
65
+ "warnings": []
66
+ }
67
+ }
@@ -0,0 +1 @@
1
+ {"name":"demo-tls","namespace":"demo","status":"available","data":{"tls.key":"forbidden"}}
@@ -0,0 +1 @@
1
+ {"name":"demo-tls","namespace":"demo","status":"healthy"}
@@ -0,0 +1 @@
1
+ {"name":"demo-tls","namespace":"demo","status":"available","notBefore":"2026-11-02T08:00:00Z","notAfter":"2026-12-02T08:00:00Z","selfSigned":true}
@@ -0,0 +1 @@
1
+ {"name":"demo-tls","namespace":"demo","status":"missing"}
@@ -16,7 +16,7 @@ import (
16
16
  "time"
17
17
  )
18
18
 
19
- const defaultUserAgent = "opensoha-contracts/0.1.27"
19
+ const defaultUserAgent = "opensoha-contracts/0.1.29"
20
20
 
21
21
  type Client struct {
22
22
  BaseURL string
@@ -50,6 +50,19 @@ func TestRuntimeAndWorkbenchLegacyEnumConstants(t *testing.T) {
50
50
  }
51
51
  }
52
52
 
53
+ func TestPersonalAgentAccessDecisionPublishedConstants(t *testing.T) {
54
+ statuses := map[PersonalAgentAccessDecisionStatus]string{
55
+ PersonalAgentAccessDecisionStatusApproved: "approved",
56
+ PersonalAgentAccessDecisionStatusRejected: "rejected",
57
+ PersonalAgentAccessDecisionStatusRevoked: "revoked",
58
+ }
59
+ for status, want := range statuses {
60
+ if string(status) != want || !status.Valid() {
61
+ t.Fatalf("PersonalAgentAccessDecisionStatus %q is not the compatible value %q", status, want)
62
+ }
63
+ }
64
+ }
65
+
53
66
  func TestObservabilityCredentialKeyLegacyTypes(t *testing.T) {
54
67
  dataSource := ObservabilityDataSource{
55
68
  CredentialKeys: []ObservabilityDataSourceCredentialKeys{
@@ -0,0 +1,40 @@
1
+ package sohaapi
2
+
3
+ import (
4
+ "encoding/json"
5
+ "testing"
6
+ )
7
+
8
+ func TestWorkloadAndCertificateSDKKeepExplicitZeroValues(t *testing.T) {
9
+ for _, tc := range []struct {
10
+ name, input string
11
+ destination any
12
+ }{
13
+ {"rollout", `{"matchesCurrentTemplate":false}`, new(KubernetesDeploymentRollout)},
14
+ {"job", `{"completionTarget":0,"suspend":false}`, new(KubernetesJob)},
15
+ {"job detail", `{"completionTarget":0,"suspend":false,"backoffLimit":0,"activeDeadlineSeconds":0,"ttlSecondsAfterFinished":0}`, new(KubernetesJobDetail)},
16
+ {"certificate", `{"selfSigned":false}`, new(KubernetesSecretCertificate)},
17
+ } {
18
+ t.Run(tc.name, func(t *testing.T) {
19
+ if err := json.Unmarshal([]byte(tc.input), tc.destination); err != nil {
20
+ t.Fatal(err)
21
+ }
22
+ encoded, err := json.Marshal(tc.destination)
23
+ if err != nil {
24
+ t.Fatal(err)
25
+ }
26
+ var before, after map[string]json.RawMessage
27
+ if err := json.Unmarshal([]byte(tc.input), &before); err != nil {
28
+ t.Fatal(err)
29
+ }
30
+ if err := json.Unmarshal(encoded, &after); err != nil {
31
+ t.Fatal(err)
32
+ }
33
+ for key, value := range before {
34
+ if string(after[key]) != string(value) {
35
+ t.Errorf("%s = %s, want %s", key, after[key], value)
36
+ }
37
+ }
38
+ })
39
+ }
40
+ }
@@ -0,0 +1,36 @@
1
+ package sohaapi
2
+
3
+ import (
4
+ "encoding/json"
5
+ "strings"
6
+ "testing"
7
+ )
8
+
9
+ func TestReasoningEffortNamesRemainCompatibleWithPodStatusEnums(t *testing.T) {
10
+ values := []WorkbenchModelOptionReasoningEfforts{WorkbenchModelOptionReasoningEffortsHigh, WorkbenchModelOptionReasoningEffortsMedium, WorkbenchModelOptionReasoningEffortsLow}
11
+ for index, want := range []string{"high", "medium", "low"} {
12
+ if string(values[index]) != want || !values[index].Valid() {
13
+ t.Fatalf("reasoning effort %d = %q, want %q", index, values[index], want)
14
+ }
15
+ }
16
+ if High != MarketplaceAdvisorySeverityHigh {
17
+ t.Fatal("legacy advisory High changed")
18
+ }
19
+ }
20
+
21
+ func TestPodStatusSDKKeepsZeroExitCode(t *testing.T) {
22
+ code := int32(0)
23
+ encoded, err := json.Marshal(KubernetesPodContainerStatus{ExitCode: &code})
24
+ if err != nil || !strings.Contains(string(encoded), `"exitCode":0`) {
25
+ t.Fatalf("successful termination was lost: %s, %v", encoded, err)
26
+ }
27
+ }
28
+
29
+ func TestPersonalEnvironmentBackendNamesRemainCompatibleWithPodStatusEnums(t *testing.T) {
30
+ values := []PersonalEnvironmentTemplateBackend{Container, VM}
31
+ for index, want := range []string{"container", "vm"} {
32
+ if string(values[index]) != want || !values[index].Valid() {
33
+ t.Fatalf("backend %d = %q, want %q", index, values[index], want)
34
+ }
35
+ }
36
+ }