@openwop/spec-artifacts 2.0.0-rc.19 → 2.0.0-rc.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.
package/CORPUS-STAMP.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_comment": "Provenance of @openwop/spec-artifacts (RFC 0168 §D.2). files: SHA-256 per file; the conformance suite compares the installed peer against dist/spec-artifacts.lock.json at start.",
3
3
  "package": "@openwop/spec-artifacts",
4
- "version": "2.0.0-rc.19",
4
+ "version": "2.0.0-rc.29",
5
5
  "corpusTag": null,
6
6
  "files": {
7
7
  "api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
@@ -9,9 +9,9 @@
9
9
  "api/grpc/openwop.proto": "c3e72bb17cba514ee98feb6434e6c9b6ea6795bfd086489ec69fd882dd1ad977",
10
10
  "api/openapi.yaml": "39081c59fb696159806b0f2f9a42e7e9ff830d622fcf2ad4159b21357580a955",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "954d7668c34a0f6132fe427f606af73999dd5b97971d1ce1f701ff8cbbf7191c",
13
- "api/v2/asyncapi.yaml": "5139025faefe214771b239a7d82b154eb225298d90017a20d28e642dc3a1d546",
14
- "api/v2/openapi.yaml": "b657a099dc5c36727ee6177face4fad66a5ac6b2110de8caa1d4ae6c1900948e",
12
+ "api/seams-v2.yaml": "db41292cd3c1c1e87270c04aacf6be9fa7726e28c25961ddcc0e5a77d69ffb2e",
13
+ "api/v2/asyncapi.yaml": "0fadad06111bdeb2a2724c448f78b9e5d9722ffc2b0b733854a168aa50321c67",
14
+ "api/v2/openapi.yaml": "2a2921c71ed5b5e9de0cf277b134b156e9a08c93b31725e799bbd38a04de8f7e",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
17
17
  "schemas/a2a-task-state.schema.json": "c9365918f993f943b4b619d42551eb066a1ed33a08d895d51b395432a5b1f1bc",
@@ -86,7 +86,7 @@
86
86
  "schemas/run-event.schema.json": "c70c06932b9831295042836213e617c8e946f20ddd49cf4e889b74d89d2175a8",
87
87
  "schemas/run-options.schema.json": "f8241ece6ddc6527de9160d811d42c231b4f77b935f2e107b3c05608545c59ab",
88
88
  "schemas/run-orchestrator-decided-event.schema.json": "17a43a3c44680a24d411141357a867b3caed6107d56de88fe2e062eaf93fa2c1",
89
- "schemas/run-snapshot.schema.json": "8a45f997beec0a2fa596f4631129a869cfbc37c9ad38583530ed11655301d2ab",
89
+ "schemas/run-snapshot.schema.json": "3343550f6973a0975c61624df569d341e843ac8592a2aa2b3b7cf540d72563ad",
90
90
  "schemas/security-advisory.schema.json": "9c550f0bf10a3193dcca4a127081fb66fe5dc1b6f729c4c17bfaea3ba3ce05fc",
91
91
  "schemas/self-hosted-runner-dispatch-frame.schema.json": "3f1d7edd572c6747657299525d8e85329b93b6fadf670e3a19c74d53e725adec",
92
92
  "schemas/self-hosted-runner-registration.schema.json": "6c781adf1bb915a9e47796494f4a527353cbbf72b480e5a05be4aac65a6d0c6e",
@@ -198,7 +198,7 @@
198
198
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
199
199
  "spec/v1/alias-detectors.json": "fee4594ef49953953ffcd0b3813300067d16b3e65ebff2aac722034ac9b3f545",
200
200
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
201
- "spec/v1/core-standard-manifest.json": "28497ea7f168ac18f721751e87ad5056579be070ef2088fa85d69282ea62cc23",
201
+ "spec/v1/core-standard-manifest.json": "722518840db1d1d9ad3ed190dcb151537860e31e92c45649307945c79eb523f1",
202
202
  "spec/v1/deprecations.json": "520d79927a8ed43ccf62c207a7cf6cf7b19b16e00bbbbdf0410667e939c19d85",
203
203
  "spec/v1/deprecations.schema.json": "4cdbbb8eb9c0092913a5704c943880f0c8581bcf0f640a7df5a84105d53c8aee",
204
204
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
@@ -228,7 +228,7 @@
228
228
  "spec/v2/path-manifest.json": "034152e09b1458c66810d4050e20a273b2b9b8fe2d92b66e8b819477de58a1be",
229
229
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
230
230
  "spec/v2/profiles.json": "1f0ee40491131cf561ef19a0394c47ba476b4567514fe16befb6cada4a10a5a4",
231
- "spec/v2/release.json": "eab43630e8b036b91589ffb275db1e172fda81c8a851fe31d833d9b54ac98e45"
231
+ "spec/v2/release.json": "38cf198fc25ff7af322a1781bd128bc31e8c213194436ba538e6e065170ddc09"
232
232
  },
233
- "corpusCommit": "c8d036fb1c060a77d066de932f9ae3b6e9cf76c2"
233
+ "corpusCommit": "9e0c76b709aed18afa85f6b84250418047861f5a"
234
234
  }
package/api/seams-v2.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP conformance seams profile (openwop-conformance-seams-v2)
4
- version: 2.0.0-rc.19
4
+ version: 2.0.0-rc.29
5
5
  description: 'GENERATED by scripts/derive-v2-api.py. RFC 0168 §C: the seams are a versioned conformance profile with their
6
6
  own document and path space (/conformance/seams/…), validated against the canonical v2 schemas with no tolerance path,
7
7
  forbidden from the capability namespace (a host advertises the profile, never a testSeams flag). This document carries
@@ -1,7 +1,7 @@
1
1
  asyncapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP v2 event streams
4
- version: 2.0.0-rc.19
4
+ version: 2.0.0-rc.29
5
5
  description: GENERATED by scripts/derive-v2-api.py (RFC 0171 §E.1, RFC 0172 §C.2). One run-events channel whose address
6
6
  is the OpenAPI path key; the server pathname is empty (bare origin). streamMode is a pattern over the closed set and its
7
7
  comma-separated combinations (`values` never combines). hostEvents has a real address (RFC 0171 §E.1) — the documented
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: OpenWOP v2 API
4
- version: 2.0.0-rc.19
4
+ version: 2.0.0-rc.29
5
5
  summary: REST surface for declaring, executing, suspending, resuming, and observing multi-step workflows.
6
6
  description: GENERATED by scripts/derive-v2-api.py from api/openapi.yaml and the RFC 0167 children (v2 charter Phase 3,
7
7
  P3-C). Bare origin, unversioned path keys, negotiation by `OpenWOP-Version` + `protocolVersions[]` (RFC 0172 §A). No seam
@@ -3046,17 +3046,18 @@ components:
3046
3046
  name: workflowId
3047
3047
  required: true
3048
3048
  schema:
3049
- type: string
3050
- minLength: 1
3051
- maxLength: 128
3049
+ $ref: ../../schemas/v2/ids.schema.json#/$defs/workflowId
3052
3050
  RunId:
3053
3051
  in: path
3054
3052
  name: runId
3055
3053
  required: true
3056
3054
  schema:
3057
- type: string
3058
- minLength: 1
3059
- maxLength: 128
3055
+ $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
3056
+ description: 'Tenant-bound `<tenantId>/<opaque>` (`identity.md` §5). The `/` is part of the
3057
+
3058
+ identifier and MUST be percent-encoded as `%2F` in the path segment.
3059
+
3060
+ '
3060
3061
  IdempotencyKey:
3061
3062
  in: header
3062
3063
  name: Idempotency-Key
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/spec-artifacts",
3
- "version": "2.0.0-rc.19",
3
+ "version": "2.0.0-rc.29",
4
4
  "description": "The OpenWOP machine-readable contract: api/ (OpenAPI, AsyncAPI, the seams profile), schemas/ (v1 and v2 JSON Schemas), the spec/v1 and spec/v2 registries (errors, event codemap, declaration, deprecations, migrations, gaps) and CORPUS-STAMP.json \u2014 published from the openwop/openwop corpus tag; the peer dependency @openwop/openwop-conformance digest-checks at start (RFC 0168 \u00a7D.2).",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -4,7 +4,11 @@
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": ["runId", "workflowId", "status"],
7
+ "required": [
8
+ "runId",
9
+ "workflowId",
10
+ "status"
11
+ ],
8
12
  "properties": {
9
13
  "runId": {
10
14
  "type": "string",
@@ -31,50 +35,82 @@
31
35
  "cancelling",
32
36
  "cancelled"
33
37
  ],
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."
38
+ "description": "Current run state. `waiting-external` MUST be used when the suspended interrupt's `kind` is `external-event` per `interrupt-profiles.md \u00a7openwop-interrupt-external-event` \u2014 distinguishes external-event waits from HITL waits at the wire level. `cancelling` (RFC 0094 \u00a7B) is the transitional state between a cancel request being accepted and the terminal `cancelled` \u2014 `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."
35
39
  },
36
40
  "compensationStatus": {
37
41
  "type": "string",
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`."
42
+ "enum": [
43
+ "none",
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`."
40
52
  },
41
53
  "owner": {
42
54
  "type": "object",
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"],
55
+ "description": "RFC 0048. The identity triple that owns this run. Redaction-safe \u2014 `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`).",
56
+ "required": [
57
+ "tenant"
58
+ ],
45
59
  "properties": {
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." },
60
+ "tenant": {
61
+ "type": "string",
62
+ "minLength": 1,
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
+ },
49
75
  "subject": {
50
76
  "$ref": "subject.schema.json",
51
77
  "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)."
52
78
  },
53
79
  "principalKind": {
54
80
  "type": "string",
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."
81
+ "enum": [
82
+ "user",
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."
57
87
  }
58
88
  },
59
89
  "additionalProperties": false
60
90
  },
61
91
  "currentNodeId": {
62
92
  "type": "string",
63
- "description": "Set when the run is suspended at a specific node (`waiting-approval` / `waiting-input` / `waiting-external`) identifies which node holds the interrupt."
93
+ "description": "Set when the run is suspended at a specific node (`waiting-approval` / `waiting-input` / `waiting-external`) \u2014 identifies which node holds the interrupt."
94
+ },
95
+ "startedAt": {
96
+ "type": "string",
97
+ "format": "date-time"
98
+ },
99
+ "completedAt": {
100
+ "type": "string",
101
+ "format": "date-time"
64
102
  },
65
- "startedAt": { "type": "string", "format": "date-time" },
66
- "completedAt": { "type": "string", "format": "date-time" },
67
103
  "agent": {
68
104
  "$ref": "agent-ref.schema.json",
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)."
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 \u2014 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)."
70
106
  },
71
107
  "runOrchestrator": {
72
108
  "$ref": "agent-ref.schema.json",
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."
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` \u2014 `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."
74
110
  },
75
111
  "nodeStates": {
76
112
  "type": "object",
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."
113
+ "description": "Per-node state map. Keys are nodeIds; values are implementation-shaped state objects. Spec doesn't constrain the inner shape \u2014 see version-negotiation.md \u00a7node-states."
78
114
  },
79
115
  "variables": {
80
116
  "type": "object",
@@ -87,26 +123,40 @@
87
123
  "error": {
88
124
  "type": "object",
89
125
  "description": "Set on terminal `failed`. Structured so consumers can route on code without parsing message text.",
90
- "required": ["code", "message"],
126
+ "required": [
127
+ "code",
128
+ "message"
129
+ ],
91
130
  "properties": {
92
- "code": { "type": "string", "minLength": 1 },
93
- "message": { "type": "string", "minLength": 1 },
94
- "details": { "type": "object" }
131
+ "code": {
132
+ "type": "string",
133
+ "minLength": 1
134
+ },
135
+ "message": {
136
+ "type": "string",
137
+ "minLength": 1
138
+ },
139
+ "details": {
140
+ "type": "object"
141
+ }
95
142
  },
96
143
  "additionalProperties": false
97
144
  },
98
145
  "engineVersion": {
99
- "type": "string",
100
- "description": "Engine version the run was started under. Used by the projection for forward-compat folds."
146
+ "type": "number",
147
+ "description": "Engine version the run was started under. Used by the projection for forward-compat folds. TYPE CORRECTED 2026-09-04: this was `string`, contradicting version-negotiation.md \u00a7Stamping \u2014 \"Every persisted run document MUST carry an `engineVersion: number` field\" \u2014 so a host obeying the prose emitted a document failing its own schema, and a host obeying the schema violated the MUST. Unimplementable as written. `spec/v1/extensions.json` already described the axis as \"a number, not a feature\", and schemas/v2 types it `integer` (RFC 0172 \u00a7B axis 3), so prose, register and v2 agreed and only this file dissented. Safe to correct: measured on both production hosts 2026-09-04, NEITHER emits the field at all, so no existing document is invalidated."
101
148
  },
102
149
  "eventLogSchemaVersion": {
103
150
  "type": "integer",
104
151
  "minimum": 0,
105
- "description": "Per-run event-log subcollection schema version. See version-negotiation.md."
152
+ "description": "Per-run event-log subcollection schema version. See version-negotiation.md. `minimum: 0` is DELIBERATE and MUST NOT be raised to 2 in v1: \u00a7Legacy detection identifies a legacy run as one whose value is \"undefined or < 2\", so a legacy document carrying 0 or 1 is contemplated by the contract and MUST still validate. schemas/v2 uses `minimum: 2` correctly, because the v2 era key admits only 2 and 3."
106
153
  },
107
154
  "tags": {
108
155
  "type": "array",
109
- "items": { "type": "string", "maxLength": 256 },
156
+ "items": {
157
+ "type": "string",
158
+ "maxLength": 256
159
+ },
110
160
  "maxItems": 100,
111
161
  "description": "Caller-supplied tags from `RunOptions.tags`."
112
162
  },
@@ -120,24 +170,44 @@
120
170
  },
121
171
  "metrics": {
122
172
  "type": "object",
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.",
173
+ "description": "Aggregate run-level metrics. Forward-compat: readers MUST tolerate missing/unknown fields. Fields are populated lazily as the engine emits them \u2014 absence does NOT mean zero. Implementation-specific fields (e.g., legacy `cost: number` estimates) MAY appear alongside the spec-canonical fields below.",
124
174
  "properties": {
125
175
  "openwopCost": {
126
176
  "type": "object",
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.",
177
+ "description": "Spec-canonical cost rollup aggregated from per-node `recordCost()` calls. Keys mirror the `openwop.cost.*` OTel attribute allowlist (see `observability.md` \u00a7Cost 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.",
128
178
  "properties": {
129
- "usd": { "type": "number", "minimum": 0, "description": "Total USD cost across all node-level recordCost emissions." },
179
+ "usd": {
180
+ "type": "number",
181
+ "minimum": 0,
182
+ "description": "Total USD cost across all node-level recordCost emissions."
183
+ },
130
184
  "tokens": {
131
185
  "type": "object",
132
186
  "properties": {
133
- "input": { "type": "integer", "minimum": 0 },
134
- "output": { "type": "integer", "minimum": 0 }
187
+ "input": {
188
+ "type": "integer",
189
+ "minimum": 0
190
+ },
191
+ "output": {
192
+ "type": "integer",
193
+ "minimum": 0
194
+ }
135
195
  },
136
196
  "additionalProperties": false
137
197
  },
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." }
198
+ "model": {
199
+ "type": "string",
200
+ "description": "Model identifier of the most recent recordCost emission (e.g., `claude-opus-4-7`). For multi-model runs, surfaces the last value."
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
+ }
141
211
  },
142
212
  "additionalProperties": false
143
213
  }
@@ -4,7 +4,7 @@
4
4
  "provenance": {
5
5
  "suite": {
6
6
  "package": "@openwop/openwop-conformance",
7
- "version": "2.0.0-rc.19"
7
+ "version": "2.0.0-rc.29"
8
8
  },
9
9
  "note": "Derived from the corpus at generation time. Regenerate with --write; verify with --check."
10
10
  },
@@ -419,5 +419,5 @@
419
419
  "$id": "https://openwop.dev/spec/v1/workspace-file.schema.json"
420
420
  }
421
421
  ],
422
- "digest": "fa76e0ac3fa483f35685edf4bdd058cb62a59c9b3975c6614f319b68d33465ae"
422
+ "digest": "3c923a9b9ff975f5014c7bcf0fd166a7852782c147df7bfb8c3ddb0491bbc172"
423
423
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "RFC 0172 \u00a7D.1 \u2014 the one release identity the v2 artifacts derive from. `version` is the next corpus tag `v<version>` (the publish workflow's coordinated-release tag pattern `v*`; RFC 0172's `openwop/v2.<minor>.<patch>` spelling is amended to this at its flip). api/v2/*.yaml info.version, the suite's 2.x version and @openwop/spec-artifacts read it. Bumped by the release PR that cuts the tag, never by hand elsewhere.",
3
- "version": "2.0.0-rc.19",
3
+ "version": "2.0.0-rc.29",
4
4
  "corpusTag": null,
5
5
  "updated": "2026-09-03"
6
6
  }