@openwop/spec-artifacts 2.0.0-rc.38 → 2.0.0-rc.6
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.
- package/CORPUS-STAMP.json +32 -34
- package/api/seams-v2.yaml +1 -1
- package/api/v2/asyncapi.yaml +1 -1
- package/api/v2/openapi.yaml +10 -9
- package/package.json +5 -18
- package/schemas/capabilities.schema.json +1 -40
- package/schemas/certification-bundle-v2.schema.json +27 -119
- package/schemas/run-snapshot.schema.json +33 -103
- package/schemas/v2/agent-deployment-transition.schema.json +6 -5
- package/schemas/v2/agent-deployment.schema.json +9 -8
- package/schemas/v2/agent-eval-suite.schema.json +12 -11
- package/schemas/v2/artifact-type-pack-manifest.schema.json +18 -22
- package/schemas/v2/capabilities.schema.json +0 -39
- package/schemas/v2/certification-bundle.schema.json +1 -1
- package/schemas/v2/chat-card-pack-manifest.schema.json +15 -19
- package/schemas/v2/debug-bundle.schema.json +6 -6
- package/schemas/v2/eval-summary.schema.json +10 -17
- package/schemas/v2/goal.schema.json +5 -5
- package/schemas/v2/ids.schema.json +22 -120
- package/schemas/v2/node-pack-manifest.schema.json +31 -27
- package/schemas/v2/proposal.schema.json +8 -7
- package/schemas/v2/run-event-payloads.schema.json +242 -246
- package/schemas/v2/run-event.schema.json +126 -2
- package/schemas/v2/run-snapshot.schema.json +13 -34
- package/schemas/v2/workflow-chain-pack-manifest.schema.json +45 -41
- package/schemas/v2/workflow-definition.schema.json +36 -31
- package/spec/v1/core-standard-manifest.json +2 -2
- package/spec/v1/deprecations.json +67 -79
- package/spec/v1/deprecations.schema.json +7 -23
- package/spec/v1/extensions.json +2 -3
- package/spec/v2/declaration.json +0 -7
- package/spec/v2/release.json +2 -2
- package/schemas/v2/webhook-delivery.schema.json +0 -26
- package/spec/v2/id-field-bindings.json +0 -104
|
@@ -4,11 +4,7 @@
|
|
|
4
4
|
"title": "RunSnapshot",
|
|
5
5
|
"description": "Projected run state returned by `GET /v1/runs/{runId}`. Source is the run's append-only event log (run-event.schema.json) folded through the `RunProjection`. Forward-compat tolerant: readers MUST ignore unknown fields rather than reject.",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"required": [
|
|
8
|
-
"runId",
|
|
9
|
-
"workflowId",
|
|
10
|
-
"status"
|
|
11
|
-
],
|
|
7
|
+
"required": ["runId", "workflowId", "status"],
|
|
12
8
|
"properties": {
|
|
13
9
|
"runId": {
|
|
14
10
|
"type": "string",
|
|
@@ -35,82 +31,50 @@
|
|
|
35
31
|
"cancelling",
|
|
36
32
|
"cancelled"
|
|
37
33
|
],
|
|
38
|
-
"description": "Current run state. `waiting-external` MUST be used when the suspended interrupt's `kind` is `external-event` per `interrupt-profiles.md
|
|
34
|
+
"description": "Current run state. `waiting-external` MUST be used when the suspended interrupt's `kind` is `external-event` per `interrupt-profiles.md §openwop-interrupt-external-event` — distinguishes external-event waits from HITL waits at the wire level. `cancelling` (RFC 0094 §B) is the transitional state between a cancel request being accepted and the terminal `cancelled` — `rest-endpoints.md` and the OpenAPI cancel responses already document the transition; a snapshot read during the cancel cascade carries it. Forward-compat: future statuses MAY be added; readers SHOULD treat unknown values as terminal-unknown rather than throw."
|
|
39
35
|
},
|
|
40
36
|
"compensationStatus": {
|
|
41
37
|
"type": "string",
|
|
42
|
-
"enum": [
|
|
43
|
-
|
|
44
|
-
"pending",
|
|
45
|
-
"running",
|
|
46
|
-
"completed",
|
|
47
|
-
"partial",
|
|
48
|
-
"failed",
|
|
49
|
-
"manual"
|
|
50
|
-
],
|
|
51
|
-
"description": "RFC 0151 \u00a7D \u2014 the run's compensation (unwind) rollup, kept separate from `status` on purpose: `status` is the FORWARD execution state and a closed union exported by the SDK, so RFC 0151 forbids reinterpreting it (there is deliberately no `compensating` run status). OPTIONAL and capability-gated \u2014 a host that does not advertise `capabilities.compensation` MUST omit this field; a host that advertises it MUST include it on every snapshot, `none` when no compensation was ever requested for the run. Presence is therefore a wire witness of the advert, and a `none` cannot be read as \"unwind is monitored here\" from a host that would never unwind. Values are the deterministic fold of the six `compensation.*` events over the persisted plan, defined normatively in `spec/v1/compensation.md \u00a7\"Run rollup: compensationStatus\"`: `none` (no `compensation.requested`), `pending` (requested, no `started`), `running` (started, plan active \u2014 a \u00a7E approval pause does NOT change it; the run's own `status: waiting-approval` carries the wait), `completed` (every inverse action completed), `partial` (plan inactive; some completed and some did not \u2014 reported, never rounded), `failed` (plan inactive; none completed), `manual` (`manual_intervention_required` recorded and unresolved; takes precedence over `partial`/`failed` while unresolved). Content-free by construction. Resolves RFC 0151 UQ3: `RunSnapshot` (`GET /v1/runs/{runId}`) is the sole owner; debug bundles and the AsyncAPI `run.snapshot` reuse it by `$ref`."
|
|
38
|
+
"enum": ["none", "pending", "running", "completed", "partial", "failed", "manual"],
|
|
39
|
+
"description": "RFC 0151 §D — the run's compensation (unwind) rollup, kept separate from `status` on purpose: `status` is the FORWARD execution state and a closed union exported by the SDK, so RFC 0151 forbids reinterpreting it (there is deliberately no `compensating` run status). OPTIONAL and capability-gated — a host that does not advertise `capabilities.compensation` MUST omit this field; a host that advertises it MUST include it on every snapshot, `none` when no compensation was ever requested for the run. Presence is therefore a wire witness of the advert, and a `none` cannot be read as \"unwind is monitored here\" from a host that would never unwind. Values are the deterministic fold of the six `compensation.*` events over the persisted plan, defined normatively in `spec/v1/compensation.md §\"Run rollup: compensationStatus\"`: `none` (no `compensation.requested`), `pending` (requested, no `started`), `running` (started, plan active — a §E approval pause does NOT change it; the run's own `status: waiting-approval` carries the wait), `completed` (every inverse action completed), `partial` (plan inactive; some completed and some did not — reported, never rounded), `failed` (plan inactive; none completed), `manual` (`manual_intervention_required` recorded and unresolved; takes precedence over `partial`/`failed` while unresolved). Content-free by construction. Resolves RFC 0151 UQ3: `RunSnapshot` (`GET /v1/runs/{runId}`) is the sole owner; debug bundles and the AsyncAPI `run.snapshot` reuse it by `$ref`."
|
|
52
40
|
},
|
|
53
41
|
"owner": {
|
|
54
42
|
"type": "object",
|
|
55
|
-
"description": "RFC 0048. The identity triple that owns this run. Redaction-safe
|
|
56
|
-
"required": [
|
|
57
|
-
"tenant"
|
|
58
|
-
],
|
|
43
|
+
"description": "RFC 0048. The identity triple that owns this run. Redaction-safe — `principal` is an opaque identifier, never PII or credential material. Optional: single-tenant hosts omit it. A principal scoped to one `workspace` MUST NOT read a run owned by another (`run_forbidden`).",
|
|
44
|
+
"required": ["tenant"],
|
|
59
45
|
"properties": {
|
|
60
|
-
"tenant": {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"description": "Top-level isolation boundary."
|
|
64
|
-
},
|
|
65
|
-
"workspace": {
|
|
66
|
-
"type": "string",
|
|
67
|
-
"minLength": 1,
|
|
68
|
-
"description": "Optional sub-tenant within the tenant (RFC 0048 workspace)."
|
|
69
|
-
},
|
|
70
|
-
"principal": {
|
|
71
|
-
"type": "string",
|
|
72
|
-
"minLength": 1,
|
|
73
|
-
"description": "Acting identity (user or agent) \u2014 opaque id, never PII."
|
|
74
|
-
},
|
|
46
|
+
"tenant": { "type": "string", "minLength": 1, "description": "Top-level isolation boundary." },
|
|
47
|
+
"workspace": { "type": "string", "minLength": 1, "description": "Optional sub-tenant within the tenant (RFC 0048 workspace)." },
|
|
48
|
+
"principal": { "type": "string", "minLength": 1, "description": "Acting identity (user or agent) — opaque id, never PII." },
|
|
75
49
|
"subject": {
|
|
76
50
|
"$ref": "subject.schema.json",
|
|
77
51
|
"description": "RFC 0165 \u00a7B. OPTIONAL issuer-scoped Subject record for the acting identity. When present: `subject.tenant` MUST equal `tenant`, `subject.subjectId` MUST equal `principal` when both are present, `subject.kind` MUST equal `principalKind` when both are present. A host that emits it on new runs synthesizes `issuer: \"urn:openwop:legacy\"` for runs that predate emission (\u00a7B.3) and copies it verbatim onto forks (\u00a7B.4)."
|
|
78
52
|
},
|
|
79
53
|
"principalKind": {
|
|
80
54
|
"type": "string",
|
|
81
|
-
"enum": [
|
|
82
|
-
|
|
83
|
-
"agent",
|
|
84
|
-
"anonymous"
|
|
85
|
-
],
|
|
86
|
-
"description": "RFC 0132. The kind of the acting principal. OPTIONAL and EXPLICIT \u2014 absent \u21d2 unconstrained (today's RFC 0048 behavior; a host that does not distinguish kinds omits it). `anonymous` \u21d2 the RFC 0132 \u00a7A anonymous-actor rules and the \u00a7C default-deny per-surface grant bind: the `principal` id MUST be opaque, origin-bound, ephemeral, non-cross-linkable, non-PII, and never inherit a role or a default-on tool baseline. A host that advertises `capabilities.anonymousActor` and dispatches through a public surface MUST set `anonymous` on the resulting run. Resolves RFC 0048 \u00a7Unresolved-Q1 for the anonymous case."
|
|
55
|
+
"enum": ["user", "agent", "anonymous"],
|
|
56
|
+
"description": "RFC 0132. The kind of the acting principal. OPTIONAL and EXPLICIT — absent ⇒ unconstrained (today's RFC 0048 behavior; a host that does not distinguish kinds omits it). `anonymous` ⇒ the RFC 0132 §A anonymous-actor rules and the §C default-deny per-surface grant bind: the `principal` id MUST be opaque, origin-bound, ephemeral, non-cross-linkable, non-PII, and never inherit a role or a default-on tool baseline. A host that advertises `capabilities.anonymousActor` and dispatches through a public surface MUST set `anonymous` on the resulting run. Resolves RFC 0048 §Unresolved-Q1 for the anonymous case."
|
|
87
57
|
}
|
|
88
58
|
},
|
|
89
59
|
"additionalProperties": false
|
|
90
60
|
},
|
|
91
61
|
"currentNodeId": {
|
|
92
62
|
"type": "string",
|
|
93
|
-
"description": "Set when the run is suspended at a specific node (`waiting-approval` / `waiting-input` / `waiting-external`)
|
|
94
|
-
},
|
|
95
|
-
"startedAt": {
|
|
96
|
-
"type": "string",
|
|
97
|
-
"format": "date-time"
|
|
98
|
-
},
|
|
99
|
-
"completedAt": {
|
|
100
|
-
"type": "string",
|
|
101
|
-
"format": "date-time"
|
|
63
|
+
"description": "Set when the run is suspended at a specific node (`waiting-approval` / `waiting-input` / `waiting-external`) — identifies which node holds the interrupt."
|
|
102
64
|
},
|
|
65
|
+
"startedAt": { "type": "string", "format": "date-time" },
|
|
66
|
+
"completedAt": { "type": "string", "format": "date-time" },
|
|
103
67
|
"agent": {
|
|
104
68
|
"$ref": "agent-ref.schema.json",
|
|
105
|
-
"description": "Optional run-level agent identity (Multi-Agent Shift Phase 1). When the run is driven by a single agent, this field carries that agent's `AgentRef`. In supervisor-orchestrated runs (Phase 5), this field rotates as workers hand off
|
|
69
|
+
"description": "Optional run-level agent identity (Multi-Agent Shift Phase 1). When the run is driven by a single agent, this field carries that agent's `AgentRef`. In supervisor-orchestrated runs (Phase 5), this field rotates as workers hand off — it always carries the active worker for the current node. See `runOrchestrator` for the run-lifetime supervisor identity. Absent for runs with no agent provenance (legacy single-actor host)."
|
|
106
70
|
},
|
|
107
71
|
"runOrchestrator": {
|
|
108
72
|
"$ref": "agent-ref.schema.json",
|
|
109
|
-
"description": "Optional orchestrator-supervisor identity (Multi-Agent Shift Phase 5). When set, this agent owns dispatch decisions across the run's lifetime; `runOrchestrator.decided` events emitted during the run carry this agent's `agentId`. Distinct from `agent`
|
|
73
|
+
"description": "Optional orchestrator-supervisor identity (Multi-Agent Shift Phase 5). When set, this agent owns dispatch decisions across the run's lifetime; `runOrchestrator.decided` events emitted during the run carry this agent's `agentId`. Distinct from `agent` — `agent` rotates with each worker; `runOrchestrator` is set at run start (or first `core.orchestrator.supervisor` node) and MUST NOT change for the run's lifetime. In single-agent runs (no supervisor), this field is absent; in supervisor runs both fields MAY co-exist."
|
|
110
74
|
},
|
|
111
75
|
"nodeStates": {
|
|
112
76
|
"type": "object",
|
|
113
|
-
"description": "Per-node state map. Keys are nodeIds; values are implementation-shaped state objects. Spec doesn't constrain the inner shape
|
|
77
|
+
"description": "Per-node state map. Keys are nodeIds; values are implementation-shaped state objects. Spec doesn't constrain the inner shape — see version-negotiation.md §node-states."
|
|
114
78
|
},
|
|
115
79
|
"variables": {
|
|
116
80
|
"type": "object",
|
|
@@ -123,40 +87,26 @@
|
|
|
123
87
|
"error": {
|
|
124
88
|
"type": "object",
|
|
125
89
|
"description": "Set on terminal `failed`. Structured so consumers can route on code without parsing message text.",
|
|
126
|
-
"required": [
|
|
127
|
-
"code",
|
|
128
|
-
"message"
|
|
129
|
-
],
|
|
90
|
+
"required": ["code", "message"],
|
|
130
91
|
"properties": {
|
|
131
|
-
"code": {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
},
|
|
135
|
-
"message": {
|
|
136
|
-
"type": "string",
|
|
137
|
-
"minLength": 1
|
|
138
|
-
},
|
|
139
|
-
"details": {
|
|
140
|
-
"type": "object"
|
|
141
|
-
}
|
|
92
|
+
"code": { "type": "string", "minLength": 1 },
|
|
93
|
+
"message": { "type": "string", "minLength": 1 },
|
|
94
|
+
"details": { "type": "object" }
|
|
142
95
|
},
|
|
143
96
|
"additionalProperties": false
|
|
144
97
|
},
|
|
145
98
|
"engineVersion": {
|
|
146
|
-
"type": "
|
|
147
|
-
"description": "Engine version the run was started under. Used by the projection for forward-compat folds.
|
|
99
|
+
"type": "string",
|
|
100
|
+
"description": "Engine version the run was started under. Used by the projection for forward-compat folds."
|
|
148
101
|
},
|
|
149
102
|
"eventLogSchemaVersion": {
|
|
150
103
|
"type": "integer",
|
|
151
104
|
"minimum": 0,
|
|
152
|
-
"description": "Per-run event-log subcollection schema version. See version-negotiation.md.
|
|
105
|
+
"description": "Per-run event-log subcollection schema version. See version-negotiation.md."
|
|
153
106
|
},
|
|
154
107
|
"tags": {
|
|
155
108
|
"type": "array",
|
|
156
|
-
"items": {
|
|
157
|
-
"type": "string",
|
|
158
|
-
"maxLength": 256
|
|
159
|
-
},
|
|
109
|
+
"items": { "type": "string", "maxLength": 256 },
|
|
160
110
|
"maxItems": 100,
|
|
161
111
|
"description": "Caller-supplied tags from `RunOptions.tags`."
|
|
162
112
|
},
|
|
@@ -170,44 +120,24 @@
|
|
|
170
120
|
},
|
|
171
121
|
"metrics": {
|
|
172
122
|
"type": "object",
|
|
173
|
-
"description": "Aggregate run-level metrics. Forward-compat: readers MUST tolerate missing/unknown fields. Fields are populated lazily as the engine emits them
|
|
123
|
+
"description": "Aggregate run-level metrics. Forward-compat: readers MUST tolerate missing/unknown fields. Fields are populated lazily as the engine emits them — absence does NOT mean zero. Implementation-specific fields (e.g., legacy `cost: number` estimates) MAY appear alongside the spec-canonical fields below.",
|
|
174
124
|
"properties": {
|
|
175
125
|
"openwopCost": {
|
|
176
126
|
"type": "object",
|
|
177
|
-
"description": "Spec-canonical cost rollup aggregated from per-node `recordCost()` calls. Keys mirror the `openwop.cost.*` OTel attribute allowlist (see `observability.md`
|
|
127
|
+
"description": "Spec-canonical cost rollup aggregated from per-node `recordCost()` calls. Keys mirror the `openwop.cost.*` OTel attribute allowlist (see `observability.md` §Cost attribution). Named `openwopCost` rather than `cost` because some implementations carry a legacy `metrics.cost: number` estimate that predates the typed rollup; using a distinct name avoids collision. Multi-provider runs report `provider`/`model` of the LAST contributing call; SDKs that need per-call detail SHOULD subscribe to OTel spans instead of reading this rollup.",
|
|
178
128
|
"properties": {
|
|
179
|
-
"usd": {
|
|
180
|
-
"type": "number",
|
|
181
|
-
"minimum": 0,
|
|
182
|
-
"description": "Total USD cost across all node-level recordCost emissions."
|
|
183
|
-
},
|
|
129
|
+
"usd": { "type": "number", "minimum": 0, "description": "Total USD cost across all node-level recordCost emissions." },
|
|
184
130
|
"tokens": {
|
|
185
131
|
"type": "object",
|
|
186
132
|
"properties": {
|
|
187
|
-
"input": {
|
|
188
|
-
|
|
189
|
-
"minimum": 0
|
|
190
|
-
},
|
|
191
|
-
"output": {
|
|
192
|
-
"type": "integer",
|
|
193
|
-
"minimum": 0
|
|
194
|
-
}
|
|
133
|
+
"input": { "type": "integer", "minimum": 0 },
|
|
134
|
+
"output": { "type": "integer", "minimum": 0 }
|
|
195
135
|
},
|
|
196
136
|
"additionalProperties": false
|
|
197
137
|
},
|
|
198
|
-
"model": {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
},
|
|
202
|
-
"provider": {
|
|
203
|
-
"type": "string",
|
|
204
|
-
"description": "Normalized provider name (e.g., `anthropic`, `openai`, `gemini`)."
|
|
205
|
-
},
|
|
206
|
-
"duration_ms": {
|
|
207
|
-
"type": "integer",
|
|
208
|
-
"minimum": 0,
|
|
209
|
-
"description": "Sum of per-call durations across all recordCost emissions in this run."
|
|
210
|
-
}
|
|
138
|
+
"model": { "type": "string", "description": "Model identifier of the most recent recordCost emission (e.g., `claude-opus-4-7`). For multi-model runs, surfaces the last value." },
|
|
139
|
+
"provider": { "type": "string", "description": "Normalized provider name (e.g., `anthropic`, `openai`, `gemini`)." },
|
|
140
|
+
"duration_ms": { "type": "integer", "minimum": 0, "description": "Sum of per-call durations across all recordCost emissions in this run." }
|
|
211
141
|
},
|
|
212
142
|
"additionalProperties": false
|
|
213
143
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://openwop.dev/spec/v2/agent-deployment-transition.schema.json",
|
|
4
4
|
"title": "AgentDeploymentTransition",
|
|
5
|
-
"description": "RFC 0082
|
|
5
|
+
"description": "RFC 0082 §E. The request body of POST /v1/agents/{agentId}/deployments — a deployment state-transition request. The host authorizes it fail-closed against the RFC 0049 `deploy:*` scope, runs any configured RFC 0051 approvalGate, enforces the RFC 0081 eval evidence when the gate requires it, and on success emits the matching `deployment.*` event and returns the updated `agent-deployment.schema.json` record. Carries no manifest body or credential material (SR-1).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"rollback",
|
|
25
25
|
"adjust-canary"
|
|
26
26
|
],
|
|
27
|
-
"description": "The lifecycle operation. `promote`: advance toward production (draft
|
|
27
|
+
"description": "The lifecycle operation. `promote`: advance toward production (draft→test→staged→active) into `toState`. `pause`: active→paused. `deprecate`: active→deprecated. `rollback`: active→rolled-back, restoring a prior version. `adjust-canary`: change the active version's `canaryPercent`. Legal transitions + the seven-state machine are normative in `agent-deployment.md` §C."
|
|
28
28
|
},
|
|
29
29
|
"toState": {
|
|
30
30
|
"type": "string",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"type": "string",
|
|
44
44
|
"minLength": 1,
|
|
45
45
|
"maxLength": 64,
|
|
46
|
-
"description": "MAY. The named deployment channel this transition targets (e.g. `stable`). The version becomes resolvable by `AgentRef.channel` = this value (RFC 0082
|
|
46
|
+
"description": "MAY. The named deployment channel this transition targets (e.g. `stable`). The version becomes resolvable by `AgentRef.channel` = this value (RFC 0082 §A/§B)."
|
|
47
47
|
},
|
|
48
48
|
"canaryPercent": {
|
|
49
49
|
"type": "integer",
|
|
@@ -52,8 +52,9 @@
|
|
|
52
52
|
"description": "MAY. For `promote` to `active` or `adjust-canary`, the share of channel traffic this version takes. A host advertising `capabilities.agents.deployment.canary: false` MUST reject any value < 100."
|
|
53
53
|
},
|
|
54
54
|
"evalRunId": {
|
|
55
|
-
"
|
|
56
|
-
"
|
|
55
|
+
"type": "string",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"description": "MAY. The RFC 0081 eval run whose `EvalSummary.passed` the gate verifies before emitting `deployment.promoted` (the §E evidence). Required when the target `approvalGate` is configured with `requiredEval`; an unmet eval gate is rejected with `eval_gate_unmet`."
|
|
57
58
|
},
|
|
58
59
|
"reason": {
|
|
59
60
|
"type": "string",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://openwop.dev/spec/v2/agent-deployment.schema.json",
|
|
4
4
|
"title": "AgentDeployment",
|
|
5
|
-
"description": "RFC 0082
|
|
5
|
+
"description": "RFC 0082 §C. A per-(agentId, version) deployment record: the host-runtime lifecycle state of one version of a manifest agent (RFC 0003/0070), distinct from the immutable AgentManifest (which is the pack-distribution descriptor) and from the registry's published semver tags (which version EXISTS, not which version SERVES). Holds the deployment `state`, the named `channels` that resolve to this version, the canary traffic share, the rollback pointer, and the provenance (`evalRunId`/`approvalGateId`) of the last transition. Content-free of any manifest body or credential material (SR-1). The record itself is host-internal; this schema is the canonical wire shape a host exposes via GET /v1/agents/{agentId}/deployments (the endpoint lands at Active → Accepted per RFC 0082 §Conformance).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"version": {
|
|
19
19
|
"type": "string",
|
|
20
20
|
"maxLength": 64,
|
|
21
|
-
"description": "The concrete agent-definition version this record governs (matches `AgentManifest.version`). A deployment record is per-(agentId, version); a `@channel` reference resolves to exactly one such version per the RFC 0082
|
|
21
|
+
"description": "The concrete agent-definition version this record governs (matches `AgentManifest.version`). A deployment record is per-(agentId, version); a `@channel` reference resolves to exactly one such version per the RFC 0082 §B pin."
|
|
22
22
|
},
|
|
23
23
|
"state": {
|
|
24
24
|
"type": "string",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"deprecated",
|
|
32
32
|
"rolled-back"
|
|
33
33
|
],
|
|
34
|
-
"description": "RFC 0082
|
|
34
|
+
"description": "RFC 0082 §C lifecycle state. `draft`: authored, not yet evaluated. `test`: undergoing eval (RFC 0081). `staged`: eval-passed, awaiting production promotion. `active`: serving (optionally at `canaryPercent < 100`). `paused`: temporarily withdrawn, recoverable. `deprecated`: sunset — no new traffic, existing pins honored. `rolled-back`: superseded; `rollbackPointer` names the version that replaced it. Legal transitions: draft→test→staged→active (promotion); active↔paused (operational); active→deprecated (terminal); active→rolled-back (recovery, with `rollbackPointer`)."
|
|
35
35
|
},
|
|
36
36
|
"canaryPercent": {
|
|
37
37
|
"type": "integer",
|
|
38
38
|
"minimum": 0,
|
|
39
39
|
"maximum": 100,
|
|
40
|
-
"description": "MAY. For an `active` version, the share (0
|
|
40
|
+
"description": "MAY. For an `active` version, the share (0–100) of channel traffic the §B pin draw assigns to THIS version; the remainder goes to the prior `active` version on the same channel. Absent ⇒ 100 (full traffic). A host advertising `agents.deployment.canary: false` MUST reject any value < 100. The per-run draw outcome is the recorded-fact `resolvedAgentVersion` (RFC 0082 §B) — never re-rolled on replay."
|
|
41
41
|
},
|
|
42
42
|
"rollbackPointer": {
|
|
43
43
|
"type": "string",
|
|
@@ -51,16 +51,17 @@
|
|
|
51
51
|
"type": "string",
|
|
52
52
|
"minLength": 1
|
|
53
53
|
},
|
|
54
|
-
"description": "MAY. The named deployment channels (e.g. `stable`, `canary`) that resolve to this version. A version MAY be on more than one channel (RFC 0082 UQ#4
|
|
54
|
+
"description": "MAY. The named deployment channels (e.g. `stable`, `canary`) that resolve to this version. A version MAY be on more than one channel (RFC 0082 UQ#4 — a promoted-to-`stable` version is also resolvable by the reserved `latest` channel = highest active semver). Channel names are host-advertised in `capabilities.agents.deployment.channels`."
|
|
55
55
|
},
|
|
56
56
|
"evalRunId": {
|
|
57
|
-
"
|
|
58
|
-
"
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1,
|
|
59
|
+
"description": "MAY. The RFC 0081 eval run whose `EvalSummary.passed` gated the last promotion into this state (the §E evidence). Content-free reference, not the eval body."
|
|
59
60
|
},
|
|
60
61
|
"approvalGateId": {
|
|
61
62
|
"type": "string",
|
|
62
63
|
"minLength": 1,
|
|
63
|
-
"description": "MAY. The RFC 0051 `approvalGate` that authorized the last transition (the
|
|
64
|
+
"description": "MAY. The RFC 0051 `approvalGate` that authorized the last transition (the §E human gate). Content-free reference."
|
|
64
65
|
}
|
|
65
66
|
},
|
|
66
67
|
"x-openwop-seeded-from": "v1"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://openwop.dev/spec/v2/agent-eval-suite.schema.json",
|
|
4
4
|
"title": "AgentEvalSuite",
|
|
5
|
-
"description": "RFC 0081
|
|
5
|
+
"description": "RFC 0081 §A. A portable, host-agnostic evaluation suite for a manifest agent (RFC 0003/0070): the tasks, the expected outputs or rubrics, the deterministic tool/memory fixtures, the allowed model classes, and the pass/fail thresholds that answer \"is this agent good enough to deploy?\". Distributed inside a pack tarball and referenced by URI exactly like `systemPromptRef` / `handoff.*SchemaRef` (RFC 0003 §C/§D) — NOT embedded in `AgentManifest`. A host advertising `capabilities.agents.evalSuite.supported: true` executes a suite as an eval run (a `mode: \"eval\"` projection over `POST /v1/runs`, RFC 0081 §B) and terminates with an `eval-summary.schema.json` scorecard. The suite carries NO secret material and NO host-internal identifiers (it is authored offline and shipped in a signed pack).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": [
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
"version": {
|
|
21
21
|
"type": "string",
|
|
22
22
|
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
|
23
|
-
"description": "SemVer of the suite. A suite version is pinned on an eval run (carried on `eval.started.suiteVersion` and `eval-summary.suiteVersion`) so a regression comparison (
|
|
23
|
+
"description": "SemVer of the suite. A suite version is pinned on an eval run (carried on `eval.started.suiteVersion` and `eval-summary.suiteVersion`) so a regression comparison (§D `regression` mode) is between like versions."
|
|
24
24
|
},
|
|
25
25
|
"targetAgentId": {
|
|
26
|
-
"
|
|
27
|
-
"
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"description": "MAY. The `AgentManifest.id` this suite is authored for. Absent ⇒ the suite is agent-agnostic and MAY be pointed at any `agentId` at run time (the run request carries the `agentId`). When present, a host SHOULD reject an eval run whose target `agentId` differs, unless the caller explicitly overrides."
|
|
28
29
|
},
|
|
29
30
|
"modes": {
|
|
30
31
|
"type": "array",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"live-shadow"
|
|
41
42
|
]
|
|
42
43
|
},
|
|
43
|
-
"description": "The eval modes this suite exercises (RFC 0081
|
|
44
|
+
"description": "The eval modes this suite exercises (RFC 0081 §D closed vocabulary). `golden`: exact / contains / json-match against each task's `expected`. `rubric`: a host-chosen judge scores against weighted criteria (nondeterministic — a recorded-fact score). `adversarial`: tasks probe for unsafe / jailbreak behavior; `safetyFindings` is the primary output. `regression`: re-run against a new agent/model/prompt version and diff scores vs a `baselineRunId` (composes RFC 0054 `:diff`). `live-shadow`: run against LIVE tools/memory instead of `fixtures` — the only mode that bypasses fixture injection; explicitly nondeterministic. A run MUST request only modes the suite declares here AND the host advertises (`capabilities.agents.evalSuite.modes`); an unadvertised mode is rejected at run-create with `400 validation_error`."
|
|
44
45
|
},
|
|
45
46
|
"allowedModels": {
|
|
46
47
|
"type": "array",
|
|
@@ -56,18 +57,18 @@
|
|
|
56
57
|
"general"
|
|
57
58
|
]
|
|
58
59
|
},
|
|
59
|
-
"description": "MAY. The `AgentManifest.modelClass` values (RFC 0002) the suite is valid for. Absent
|
|
60
|
+
"description": "MAY. The `AgentManifest.modelClass` values (RFC 0002) the suite is valid for. Absent ⇒ valid for any class. A host SHOULD record the `evaluatedModelClass` on the summary so a score is interpreted against the model it was produced with."
|
|
60
61
|
},
|
|
61
62
|
"thresholds": {
|
|
62
63
|
"type": "object",
|
|
63
64
|
"additionalProperties": false,
|
|
64
|
-
"description": "MAY. The pass/fail bar for the suite. A task or the aggregate `passed` flag is computed against these. Absent
|
|
65
|
+
"description": "MAY. The pass/fail bar for the suite. A task or the aggregate `passed` flag is computed against these. Absent ⇒ the host's default bar (the summary still carries raw scores).",
|
|
65
66
|
"properties": {
|
|
66
67
|
"passScore": {
|
|
67
68
|
"type": "number",
|
|
68
69
|
"minimum": 0,
|
|
69
70
|
"maximum": 1,
|
|
70
|
-
"description": "The minimum aggregate score (0.0
|
|
71
|
+
"description": "The minimum aggregate score (0.0–1.0) for `EvalSummary.passed: true`."
|
|
71
72
|
},
|
|
72
73
|
"maxCostUsd": {
|
|
73
74
|
"type": "number",
|
|
@@ -100,7 +101,7 @@
|
|
|
100
101
|
"description": "Suite-unique task identifier (kebab-case). Carried verbatim on `eval.scored.taskId` and the per-task summary entry."
|
|
101
102
|
},
|
|
102
103
|
"input": {
|
|
103
|
-
"description": "The run input for the task, validated against the agent's input schema by the host. An opaque object/value
|
|
104
|
+
"description": "The run input for the task, validated against the agent's input schema by the host. An opaque object/value — content is task-defined."
|
|
104
105
|
},
|
|
105
106
|
"expected": {
|
|
106
107
|
"type": "object",
|
|
@@ -144,7 +145,7 @@
|
|
|
144
145
|
"rubric": {
|
|
145
146
|
"type": "array",
|
|
146
147
|
"minItems": 1,
|
|
147
|
-
"description": "Present when `kind: \"rubric\"`. Weighted criteria a judge scores the output against; the task score is the weighted sum of met criteria, normalized to 0.0
|
|
148
|
+
"description": "Present when `kind: \"rubric\"`. Weighted criteria a judge scores the output against; the task score is the weighted sum of met criteria, normalized to 0.0–1.0. Judge selection + scoring is host-internal (nondeterministic — the score is a recorded fact).",
|
|
148
149
|
"items": {
|
|
149
150
|
"type": "object",
|
|
150
151
|
"additionalProperties": false,
|
|
@@ -172,7 +173,7 @@
|
|
|
172
173
|
"fixtures": {
|
|
173
174
|
"type": "object",
|
|
174
175
|
"additionalProperties": false,
|
|
175
|
-
"description": "MAY. Deterministic substitutes for live tool/memory I/O so a `golden`/`regression` eval is reproducible. When present, the eval host MUST inject `toolResponses` in place of live tool calls and seed `memorySeed` before the invocation. The `live-shadow` mode is the explicit exception
|
|
176
|
+
"description": "MAY. Deterministic substitutes for live tool/memory I/O so a `golden`/`regression` eval is reproducible. When present, the eval host MUST inject `toolResponses` in place of live tool calls and seed `memorySeed` before the invocation. The `live-shadow` mode is the explicit exception — it ignores `fixtures` and runs against live tools/memory.",
|
|
176
177
|
"properties": {
|
|
177
178
|
"toolResponses": {
|
|
178
179
|
"type": "array",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://openwop.dev/spec/v2/artifact-type-pack-manifest.schema.json",
|
|
4
4
|
"title": "ArtifactTypePackManifest",
|
|
5
|
-
"description": "Manifest for a published OpenWOP artifact-type pack
|
|
5
|
+
"description": "Manifest for a published OpenWOP artifact-type pack — `pack.json` at the pack root with `kind: \"artifact-type\"`. Peer to `node-pack-manifest.schema.json` (RFC 0003), `workflow-chain-pack-manifest.schema.json` (RFC 0013), and `prompt-pack-manifest.schema.json` (RFC 0028); disjoint from all three via the `kind` discriminator. See `spec/v1/artifact-type-packs.md` for the canonical contract and RFC 0071 for the rationale.\n\nArtifact-type packs distribute typed artifact definitions — the JSON Schema, rendering hint, lifecycle, and export-format hints for the rich outputs workflow nodes produce — via the same signed-tarball + Ed25519 + SRI pipeline that already serves node, workflow-chain, and prompt packs. When a host installs an artifact-type pack and advertises `host.artifactTypes: { supported: true }`, it validates produced artifacts of the declared `artifactTypeId`s against their `schemaRef` before emitting `artifact.created`.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"name",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"name": {
|
|
22
22
|
"type": "string",
|
|
23
|
-
"description": "Reverse-DNS pack name per `node-packs.md`
|
|
23
|
+
"description": "Reverse-DNS pack name per `node-packs.md` §Naming. Reserved scopes are identical (`core.*` / `vendor.<org>.*` / `community.<author>.*` / `private.<host>.*`). Mirror of `prompt-pack-manifest.schema.json#/properties/name`.",
|
|
24
24
|
"pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
|
|
25
25
|
"minLength": 1,
|
|
26
26
|
"maxLength": 256
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"properties": {
|
|
66
66
|
"openwop": {
|
|
67
67
|
"type": "string",
|
|
68
|
-
"description": "Semver range
|
|
68
|
+
"description": "Semver range — which openwop protocol versions this pack works against. Example: `>=1.1 <2.0.0`."
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
"additionalProperties": false
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"additionalProperties": {
|
|
76
76
|
"type": "string"
|
|
77
77
|
},
|
|
78
|
-
"description": "Other packs this pack depends on. Map of pack name
|
|
78
|
+
"description": "Other packs this pack depends on. Map of pack name → semver range. Resolved transitively at workflow-register time."
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
81
|
"type": "object",
|
|
@@ -106,25 +106,21 @@
|
|
|
106
106
|
"additionalProperties": false,
|
|
107
107
|
"properties": {
|
|
108
108
|
"artifactTypeId": {
|
|
109
|
-
"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
"pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$"
|
|
115
|
-
}
|
|
116
|
-
],
|
|
117
|
-
"description": "Reverse-DNS artifact-type identifier. Same pattern and reserved scopes as a pack `name`. This is the value `WorkflowNode.artifactType`, `nodes[].artifact.typeId`, and `artifact.created.artifactType` reference. Third parties MUST NOT publish under `core.*`."
|
|
109
|
+
"type": "string",
|
|
110
|
+
"description": "Reverse-DNS artifact-type identifier. Same pattern and reserved scopes as a pack `name`. This is the value `WorkflowNode.artifactType`, `nodes[].artifact.typeId`, and `artifact.created.artifactType` reference. Third parties MUST NOT publish under `core.*`.",
|
|
111
|
+
"pattern": "^(core|vendor|community|private)\\.[a-z][a-z0-9_-]*(\\.[a-z][a-zA-Z0-9_-]*)+$",
|
|
112
|
+
"minLength": 1,
|
|
113
|
+
"maxLength": 256
|
|
118
114
|
},
|
|
119
115
|
"schemaRef": {
|
|
120
116
|
"type": "string",
|
|
121
117
|
"minLength": 1,
|
|
122
|
-
"description": "Path inside the pack tarball to the artifact's JSON Schema (Draft 2020-12). The target schema MUST set `additionalProperties: false` at its top level and declare an `$id` under `{HostBase}/schemas/artifacts/{artifactTypeId}.schema.json`, mirroring the envelope convention in `ai-envelope.md`
|
|
118
|
+
"description": "Path inside the pack tarball to the artifact's JSON Schema (Draft 2020-12). The target schema MUST set `additionalProperties: false` at its top level and declare an `$id` under `{HostBase}/schemas/artifacts/{artifactTypeId}.schema.json`, mirroring the envelope convention in `ai-envelope.md` §\"Canonical schema location\"."
|
|
123
119
|
},
|
|
124
120
|
"schemaVersion": {
|
|
125
121
|
"type": "integer",
|
|
126
122
|
"minimum": 0,
|
|
127
|
-
"description": "Non-negative integer artifact-schema version, parallel to the per-kind integer in `capabilities.schemaVersions`. Absent
|
|
123
|
+
"description": "Non-negative integer artifact-schema version, parallel to the per-kind integer in `capabilities.schemaVersions`. Absent ⇒ treated as 0. Bumped when the artifact schema changes shape. A version *declaration*, not a validation guarantee (RFC 0075 / P1-2)."
|
|
128
124
|
},
|
|
129
125
|
"validation": {
|
|
130
126
|
"type": "string",
|
|
@@ -132,7 +128,7 @@
|
|
|
132
128
|
"open",
|
|
133
129
|
"closed"
|
|
134
130
|
],
|
|
135
|
-
"description": "RFC 0075. Strictness contract of the artifact's `schemaRef` schema. `closed`
|
|
131
|
+
"description": "RFC 0075. Strictness contract of the artifact's `schemaRef` schema. `closed` ⇒ closed-world (`additionalProperties: false`); a consumer MAY rely on the absence of unknown fields. `open` (recommended for AI/LLM-produced artifacts) ⇒ the schema tolerates extra fields to absorb model drift; consumers MUST ignore unknown fields per COMPATIBILITY.md §2.1. Absent ⇒ `open` (the forward-compatible default). The `additionalProperties:false` requirement on `schemaRef` was relaxed MUST → SHOULD by RFC 0075: a closed-world artifact contract contradicts the protocol's own forward-compat mandate and the first AI-native adopter cannot meet it."
|
|
136
132
|
},
|
|
137
133
|
"displayName": {
|
|
138
134
|
"type": "string",
|
|
@@ -150,7 +146,7 @@
|
|
|
150
146
|
"pattern": "^([a-z][a-z0-9]*|vendor\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*|x-[a-z][a-z0-9-]*)$"
|
|
151
147
|
},
|
|
152
148
|
"uniqueItems": true,
|
|
153
|
-
"description": "Export-format identifiers (hints) a renderer MAY offer. Spec-reserved core identifiers carry interoperable meaning (the lowercase file-extension / common name): `pdf`, `pptx`, `docx`, `xlsx`, `md`, `html`, `txt`, `csv`, `json`, `png`, `svg`, `jpeg`, `step`, `stl`, `dxf`. Domain-specific formats outside the core set MUST be `vendor.<org>.<format>`- or `x-<format>`-prefixed (mirrors the `requiredModelCapabilities` reserved-core + extension idiom). Advisory: this spec assigns no byte-level production semantics to any identifier
|
|
149
|
+
"description": "Export-format identifiers (hints) a renderer MAY offer. Spec-reserved core identifiers carry interoperable meaning (the lowercase file-extension / common name): `pdf`, `pptx`, `docx`, `xlsx`, `md`, `html`, `txt`, `csv`, `json`, `png`, `svg`, `jpeg`, `step`, `stl`, `dxf`. Domain-specific formats outside the core set MUST be `vendor.<org>.<format>`- or `x-<format>`-prefixed (mirrors the `requiredModelCapabilities` reserved-core + extension idiom). Advisory: this spec assigns no byte-level production semantics to any identifier — it standardizes the identifier so two hosts agree what `pptx` names, not how the bytes are produced."
|
|
154
150
|
},
|
|
155
151
|
"syncOn": {
|
|
156
152
|
"type": "string",
|
|
@@ -171,19 +167,19 @@
|
|
|
171
167
|
},
|
|
172
168
|
"diffable": {
|
|
173
169
|
"type": "boolean",
|
|
174
|
-
"description": "True if the artifact's schema supports structural diffing (informs run-diff tooling per rest-endpoints.md
|
|
170
|
+
"description": "True if the artifact's schema supports structural diffing (informs run-diff tooling per rest-endpoints.md §:diff). Non-normative."
|
|
175
171
|
}
|
|
176
172
|
},
|
|
177
173
|
"patternProperties": {
|
|
178
174
|
"^(openwop-|x-|vendor\\.)": {
|
|
179
|
-
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md`
|
|
175
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
180
176
|
}
|
|
181
177
|
}
|
|
182
178
|
},
|
|
183
179
|
"RenderingHint": {
|
|
184
180
|
"type": "object",
|
|
185
181
|
"additionalProperties": false,
|
|
186
|
-
"description": "Advisory rendering hint. Reuses the closed vocabulary defined in ai-envelope.md
|
|
182
|
+
"description": "Advisory rendering hint. Reuses the closed vocabulary defined in ai-envelope.md §\"Rendering hints\" (RFC 0055). Advisory only — consumers MUST degrade gracefully and MUST NOT treat this as a validation input. The `card` display value is reserved for envelopes and is excluded here (durable artifacts are not transient chat cards).",
|
|
187
183
|
"properties": {
|
|
188
184
|
"display": {
|
|
189
185
|
"type": "string",
|
|
@@ -218,7 +214,7 @@
|
|
|
218
214
|
},
|
|
219
215
|
"Signing": {
|
|
220
216
|
"type": "object",
|
|
221
|
-
"description": "Optional signing metadata. See node-packs.md
|
|
217
|
+
"description": "Optional signing metadata. See node-packs.md §signing.",
|
|
222
218
|
"additionalProperties": false,
|
|
223
219
|
"properties": {
|
|
224
220
|
"publicKeyRef": {
|
|
@@ -242,7 +238,7 @@
|
|
|
242
238
|
},
|
|
243
239
|
"patternProperties": {
|
|
244
240
|
"^(openwop-|x-|vendor\\.)": {
|
|
245
|
-
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md`
|
|
241
|
+
"description": "Vendor / host extension escape hatch (RFC 0138). A property matching `^(x-|vendor\\.)` is a HOST EXTENSION: its semantics are unspecified by this protocol, and a consumer that does not recognize one MUST ignore it rather than reject the pack. Resolves the contradiction in `host-extensions.md` §\"Vendor-prefixed namespaces\", which requires a client receiving an unknown vendor-prefixed field to treat it as opaque — a field that `additionalProperties: false` previously made impossible to carry. SECURITY: an extension value is PACK-AUTHORED, therefore untrusted; \"ignore\" means ignore. See `node-packs.md` §\"Vendor extensions on pack manifests\" and the `pack-manifest-extension-opaque` invariant."
|
|
246
242
|
}
|
|
247
243
|
},
|
|
248
244
|
"x-openwop-seeded-from": "v1"
|
|
@@ -232,45 +232,6 @@
|
|
|
232
232
|
"additionalProperties": true,
|
|
233
233
|
"x-openwop-seeded-from": "v1"
|
|
234
234
|
},
|
|
235
|
-
"signingKeys": {
|
|
236
|
-
"type": "array",
|
|
237
|
-
"description": "RFC 0168 §E.2 — the public keys this host signs certification bundles with. A bundle signature names a keyId; a verifier resolves it HERE, in the discovery document of the host the bundle is about, and checks the Ed25519 signature with the matching publicKey. Without this array a signature attests integrity only: it proves the bundle was not altered after signing and says nothing about who signed it, because a signer can mint a keypair and a keyId at will. RFC 0168 disposed of \"an Ed25519 attestation without a key registry is a signature nobody can check\" by naming this surface; this is that surface.",
|
|
238
|
-
"items": {
|
|
239
|
-
"type": "object",
|
|
240
|
-
"additionalProperties": false,
|
|
241
|
-
"required": [
|
|
242
|
-
"keyId",
|
|
243
|
-
"alg",
|
|
244
|
-
"publicKey"
|
|
245
|
-
],
|
|
246
|
-
"properties": {
|
|
247
|
-
"keyId": {
|
|
248
|
-
"$ref": "ids.schema.json#/$defs/keyId",
|
|
249
|
-
"description": "The identifier a bundle signature carries in signature.keyId. Unique within this array."
|
|
250
|
-
},
|
|
251
|
-
"alg": {
|
|
252
|
-
"const": "ed25519",
|
|
253
|
-
"description": "RFC 0168 §E.2 fixes the attestation algorithm; no other value is defined."
|
|
254
|
-
},
|
|
255
|
-
"publicKey": {
|
|
256
|
-
"type": "string",
|
|
257
|
-
"pattern": "^[A-Za-z0-9_-]{43}$",
|
|
258
|
-
"description": "The Ed25519 public key, base64url, unpadded (32 bytes) — the half that verifies, never the half that signs."
|
|
259
|
-
},
|
|
260
|
-
"use": {
|
|
261
|
-
"enum": [
|
|
262
|
-
"certification-bundle"
|
|
263
|
-
],
|
|
264
|
-
"description": "What the key signs. Closed, so a future use is an explicit addition rather than a reinterpretation of a key already published for something else."
|
|
265
|
-
},
|
|
266
|
-
"retiredAt": {
|
|
267
|
-
"type": "string",
|
|
268
|
-
"format": "date-time",
|
|
269
|
-
"description": "When the key stopped signing. A retired key stays listed so bundles it already signed remain verifiable; dropping it would silently invalidate historical evidence, which is the opposite of what an evidence trail is for."
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
235
|
"discovery": {
|
|
275
236
|
"type": "object",
|
|
276
237
|
"description": "Discovery advertisement (capabilities-change-detection.md). Optional in v1; absence means the host serves only the public unauthenticated payload at /.well-known/openwop. Landed by RFC 0011.",
|