@openwop/openwop-conformance 2.46.7 → 2.46.8

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/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [2.46.8] — 2026-10-14 — RFC 0244: eval trajectories, repeated trials, fail-closed fixture miss
2
+
3
+ - **New `v2-eval-trajectory-trials`:**
4
+ - Server-free: the RFC 0244 suite, summary and `eval.*` fields validate; the negatives and the content-free negatives (`eval-summary-no-content-leak`) are refused; the three eval-suite fixtures validate.
5
+ - Host legs, each gated on its `agents.evalSuite` member (`trajectory`, `trials`, `fixtureMatching`) and its fixture: `0244.trajectory-graded`, `0244.trials-per-task`, `0244.trial-stats-formula`, `0244.fixture-miss-fails`, and `0244.unadvertised-feature-refused` (a trials suite on a host without `trials` → `400 validation_error`).
6
+ - **New `lib/eval-trajectory.ts`:** the reference grader the host legs compare against, with self-tests (the four modes, `forbidden`, `argsMatch`, the mode-independent `matchedCount`, and the §B formulas over a fixed `(n, c, k)` table).
7
+ - **New `fixtures/eval-suites/`:** `conformance-eval-trajectory`, `conformance-eval-trials`, `conformance-eval-fixture-miss`. These are `AgentEvalSuite` documents run against the suite-defined scripted agent `core.conformance.agents.scripted` (contract in `fixtures.md`).
8
+ - **What a host's next bundle shows:** no host advertises the new members or seeds the fixtures, so every host leg is `inapplicable`. The server-free legs pass everywhere.
9
+
1
10
  ## [2.46.7] — 2026-10-14 — one id per leg; RFC 0243 G2 closes
2
11
 
3
12
  - **RFC 0243 G2 closes.** No assertion changes; the packed `spec/v1/gaps.json` changes. openwop-app's certified 2.46.6 cut (build `44960bca5`) records all three G2 ids `executed-pass`.
package/README.md CHANGED
@@ -137,7 +137,7 @@ Each scenario file records its disposition and assertion count in `<report-dir>/
137
137
 
138
138
  ## What's Covered
139
139
 
140
- The current suite has 657 scenario files under `src/scenarios/`. By target major ([`scenario-majors.json`](./scenario-majors.json)): 217 files run against a v2 host and 455 against a v1 host; 15 of them run at both.
140
+ The current suite has 658 scenario files under `src/scenarios/`. By target major ([`scenario-majors.json`](./scenario-majors.json)): 218 files run against a v2 host and 455 against a v1 host; 15 of them run at both.
141
141
 
142
142
  The v2 scenarios (`src/scenarios/v2-*.test.ts`) cover, by area:
143
143
 
@@ -166,7 +166,7 @@ Three kinds of scenario share the tree:
166
166
  - **Host** checks drive a live host over HTTP.
167
167
  - **Gated** checks run only when the host advertises the family, profile or fixture they need, and otherwise record `inapplicable` or `blocked` with a reason. `--require-behavior` turns an advertised-but-unwitnessable behaviour into a failure.
168
168
 
169
- Current source tree: 657 scenario files. [`coverage.md`](./coverage.md) maps each spec document and every OpenAPI operation to its scenarios. [`CHANGELOG.md`](./CHANGELOG.md) records when each scenario landed.
169
+ Current source tree: 658 scenario files. [`coverage.md`](./coverage.md) maps each spec document and every OpenAPI operation to its scenarios. [`CHANGELOG.md`](./CHANGELOG.md) records when each scenario landed.
170
170
 
171
171
  ## How the 2.x suite is built
172
172
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.46.7",
4
- "stampSha256": "aa3b7b3012274a6d025e75053ff2697d94062a06f4dd2fee48e2ef01964f8581"
3
+ "version": "2.46.8",
4
+ "stampSha256": "79a4cd8b042b09bc0065a6c8711b517f0b95b1ff96031f07f15eef6663fc7d8f"
5
5
  }
@@ -0,0 +1,82 @@
1
+ {
2
+ "suiteId": "core.openwop.evals.conformance-eval-fixture-miss",
3
+ "version": "1.0.0",
4
+ "targetAgentId": "core.conformance.agents.scripted",
5
+ "modes": [
6
+ "golden"
7
+ ],
8
+ "tasks": [
9
+ {
10
+ "taskId": "served",
11
+ "input": {
12
+ "script": [
13
+ {
14
+ "tool": "conformance.eval:lookup",
15
+ "arguments": {
16
+ "orderId": "o-1"
17
+ }
18
+ }
19
+ ],
20
+ "output": "done"
21
+ },
22
+ "expected": {
23
+ "kind": "golden",
24
+ "match": {
25
+ "strategy": "contains",
26
+ "value": "done"
27
+ }
28
+ },
29
+ "fixtures": {
30
+ "onMiss": "fail",
31
+ "toolResponses": [
32
+ {
33
+ "tool": "conformance.eval:lookup",
34
+ "args": {
35
+ "orderId": "o-1"
36
+ },
37
+ "argsMatch": "exact",
38
+ "response": {
39
+ "status": "shipped"
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ },
45
+ {
46
+ "taskId": "missed",
47
+ "input": {
48
+ "script": [
49
+ {
50
+ "tool": "conformance.eval:lookup",
51
+ "arguments": {
52
+ "orderId": "o-2"
53
+ }
54
+ }
55
+ ],
56
+ "output": "done"
57
+ },
58
+ "expected": {
59
+ "kind": "golden",
60
+ "match": {
61
+ "strategy": "contains",
62
+ "value": "done"
63
+ }
64
+ },
65
+ "fixtures": {
66
+ "onMiss": "fail",
67
+ "toolResponses": [
68
+ {
69
+ "tool": "conformance.eval:lookup",
70
+ "args": {
71
+ "orderId": "o-1"
72
+ },
73
+ "argsMatch": "exact",
74
+ "response": {
75
+ "status": "shipped"
76
+ }
77
+ }
78
+ ]
79
+ }
80
+ }
81
+ ]
82
+ }
@@ -0,0 +1,202 @@
1
+ {
2
+ "suiteId": "core.openwop.evals.conformance-eval-trajectory",
3
+ "version": "1.0.0",
4
+ "targetAgentId": "core.conformance.agents.scripted",
5
+ "modes": [
6
+ "golden"
7
+ ],
8
+ "thresholds": {
9
+ "passScore": 0.5
10
+ },
11
+ "tasks": [
12
+ {
13
+ "taskId": "in-order-match",
14
+ "input": {
15
+ "script": [
16
+ {
17
+ "tool": "conformance.eval:lookup",
18
+ "arguments": {
19
+ "orderId": "o-1"
20
+ }
21
+ },
22
+ {
23
+ "tool": "conformance.eval:note",
24
+ "arguments": {
25
+ "text": "checked"
26
+ }
27
+ },
28
+ {
29
+ "tool": "conformance.eval:refund",
30
+ "arguments": {
31
+ "orderId": "o-1",
32
+ "amountMinor": 4200
33
+ }
34
+ }
35
+ ],
36
+ "output": "done"
37
+ },
38
+ "expected": {
39
+ "kind": "golden",
40
+ "match": {
41
+ "strategy": "contains",
42
+ "value": "done"
43
+ },
44
+ "trajectory": {
45
+ "match": "in-order",
46
+ "calls": [
47
+ {
48
+ "tool": "conformance.eval:lookup"
49
+ },
50
+ {
51
+ "tool": "conformance.eval:refund",
52
+ "args": {
53
+ "amountMinor": 4200
54
+ },
55
+ "argsMatch": "subset"
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ },
61
+ {
62
+ "taskId": "in-order-wrong-order",
63
+ "input": {
64
+ "script": [
65
+ {
66
+ "tool": "conformance.eval:refund",
67
+ "arguments": {
68
+ "orderId": "o-1",
69
+ "amountMinor": 4200
70
+ }
71
+ },
72
+ {
73
+ "tool": "conformance.eval:lookup",
74
+ "arguments": {
75
+ "orderId": "o-1"
76
+ }
77
+ }
78
+ ],
79
+ "output": "done"
80
+ },
81
+ "expected": {
82
+ "kind": "golden",
83
+ "match": {
84
+ "strategy": "contains",
85
+ "value": "done"
86
+ },
87
+ "trajectory": {
88
+ "match": "in-order",
89
+ "calls": [
90
+ {
91
+ "tool": "conformance.eval:lookup"
92
+ },
93
+ {
94
+ "tool": "conformance.eval:refund"
95
+ }
96
+ ]
97
+ }
98
+ }
99
+ },
100
+ {
101
+ "taskId": "forbidden-hit",
102
+ "input": {
103
+ "script": [
104
+ {
105
+ "tool": "conformance.eval:lookup",
106
+ "arguments": {
107
+ "orderId": "o-1"
108
+ }
109
+ },
110
+ {
111
+ "tool": "conformance.eval:refund-all",
112
+ "arguments": {}
113
+ }
114
+ ],
115
+ "output": "done"
116
+ },
117
+ "expected": {
118
+ "kind": "golden",
119
+ "match": {
120
+ "strategy": "contains",
121
+ "value": "done"
122
+ },
123
+ "trajectory": {
124
+ "match": "any-order",
125
+ "calls": [
126
+ {
127
+ "tool": "conformance.eval:lookup"
128
+ }
129
+ ],
130
+ "forbidden": [
131
+ "conformance.eval:refund-all"
132
+ ]
133
+ }
134
+ }
135
+ },
136
+ {
137
+ "taskId": "trajectory-only-subset",
138
+ "input": {
139
+ "script": [
140
+ {
141
+ "tool": "conformance.eval:lookup",
142
+ "arguments": {
143
+ "orderId": "o-1"
144
+ }
145
+ },
146
+ {
147
+ "tool": "conformance.eval:lookup",
148
+ "arguments": {
149
+ "orderId": "o-2"
150
+ }
151
+ }
152
+ ],
153
+ "output": "done"
154
+ },
155
+ "expected": {
156
+ "kind": "trajectory",
157
+ "trajectory": {
158
+ "match": "subset",
159
+ "calls": [
160
+ {
161
+ "tool": "conformance.eval:lookup"
162
+ },
163
+ {
164
+ "tool": "conformance.eval:refund"
165
+ }
166
+ ]
167
+ }
168
+ }
169
+ },
170
+ {
171
+ "taskId": "exact-args-mismatch",
172
+ "input": {
173
+ "script": [
174
+ {
175
+ "tool": "conformance.eval:refund",
176
+ "arguments": {
177
+ "orderId": "o-1",
178
+ "amountMinor": 100
179
+ }
180
+ }
181
+ ],
182
+ "output": "done"
183
+ },
184
+ "expected": {
185
+ "kind": "trajectory",
186
+ "trajectory": {
187
+ "match": "exact",
188
+ "calls": [
189
+ {
190
+ "tool": "conformance.eval:refund",
191
+ "args": {
192
+ "orderId": "o-1",
193
+ "amountMinor": 4200
194
+ },
195
+ "argsMatch": "exact"
196
+ }
197
+ ]
198
+ }
199
+ }
200
+ }
201
+ ]
202
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "suiteId": "core.openwop.evals.conformance-eval-trials",
3
+ "version": "1.0.0",
4
+ "targetAgentId": "core.conformance.agents.scripted",
5
+ "modes": [
6
+ "golden"
7
+ ],
8
+ "trials": {
9
+ "count": 3,
10
+ "k": 2,
11
+ "passRule": "all"
12
+ },
13
+ "tasks": [
14
+ {
15
+ "taskId": "always-passes",
16
+ "input": {
17
+ "script": [
18
+ {
19
+ "tool": "conformance.eval:lookup",
20
+ "arguments": {
21
+ "orderId": "o-1"
22
+ }
23
+ }
24
+ ],
25
+ "output": "done"
26
+ },
27
+ "expected": {
28
+ "kind": "golden",
29
+ "match": {
30
+ "strategy": "contains",
31
+ "value": "done"
32
+ }
33
+ }
34
+ },
35
+ {
36
+ "taskId": "always-fails",
37
+ "input": {
38
+ "script": [
39
+ {
40
+ "tool": "conformance.eval:lookup",
41
+ "arguments": {
42
+ "orderId": "o-1"
43
+ }
44
+ }
45
+ ],
46
+ "output": "done"
47
+ },
48
+ "expected": {
49
+ "kind": "golden",
50
+ "match": {
51
+ "strategy": "contains",
52
+ "value": "never-in-output"
53
+ }
54
+ }
55
+ }
56
+ ]
57
+ }
package/fixtures.md CHANGED
@@ -69,6 +69,9 @@ All fixtures MUST advertise:
69
69
  | Prompt All Four Kinds | `conformance-prompt-all-four-kinds` | RFC 0027 §A four-kind dispatch coverage with a MULTI-ENTRY `fewShotPromptRefs[]` array. Single `mock-ai` node with one ref per singular-kind slot (`systemPromptRef`, `userPromptRef`, `schemaHintPromptRef`) + two distinct templateIds in `fewShotPromptRefs[]`; host MUST emit 5 `agent.promptResolved` events (one per slot) AND 5 `prompt.composed` events. Multi-entry few-shot is the regression pin for `fewShotPromptRefs[slotIndex]` per-index resolution — a host that hard-codes `[0]` would emit the same template twice in the few-shot events and fail the per-templateId assertion. Capability-gated on `capabilities.prompts.supported`. | `completed` | ≤ 10s |
70
70
  | Dispatch Loop | `conformance-dispatch-loop` | Verifies `core.dispatch` loop mechanism | `completed` | ≤ 30s |
71
71
  | Dispatch Fan-Out Race | `conformance-dispatch-fanout-race` | RFC 0243 §A.2 — a `core.dispatch` node pinning `fanOutPolicy: 'parallel'` with a `race` join. A host that does not admit it refuses it at load, or at `createRun` with `400 validation_error` and `details.requiredCapability` (`v2-agents-fan-out`) | refused; no run | ≤ 5s |
72
+ | Eval — Trajectory | `conformance-eval-trajectory` | RFC 0244 §A — an `AgentEvalSuite` under `eval-suites/` (not a workflow), run with `mode: eval` against the scripted agent; five tasks whose tool paths match / miss each mode and a forbidden call (`v2-eval-trajectory-trials`) | eval run `completed`; per-task grade as the reference grader | ≤ 30s |
73
+ | Eval — Trials | `conformance-eval-trials` | RFC 0244 §B — two tasks × `trials.count: 3`, `k: 2`; one always passes, one always fails (`v2-eval-trajectory-trials`) | eval run `completed`, or `400` where `trials` is unadvertised | ≤ 30s |
74
+ | Eval — Fixture Miss | `conformance-eval-fixture-miss` | RFC 0244 §D — `onMiss: "fail"`; one task served by an args-matched fixture, one that misses it (`v2-eval-trajectory-trials`) | eval run `completed`; the missed task fails | ≤ 30s |
72
75
  | Dispatch Fan-Out Waves | `conformance-dispatch-fanout-waves` | RFC 0243 §A.3 — a parallel `core.dispatch` (`maxConcurrency: 2`, `wait-all`/`collect`) whose supervisor's `mockDispatchPlan` names three `conformance-delay` children. At most `min(2, maxFanOut)` run at once and none is dropped. Gated on `agents.fanOut` admitting `wait-all` with `collect`. | `completed` | ≤ 30s |
73
76
  | Dispatch Per-Item Input | `conformance-dispatch-per-item` | RFC 0243 §A.4 — a sequential `core.dispatch` whose decision dispatches `conformance-dispatch-input-mapping-child` twice with `nextWorkerInputs` `item-a`, `item-b`. At `agents.fanOut.perItemInput: true` each child receives its item; otherwise the dispatch node fails `validation_error` and no child is dispatched. | `completed`, or `failed` without `perItemInput` | ≤ 30s |
74
77
  | Dispatch Per-Item Length Mismatch | `conformance-dispatch-per-item-mismatch` | `orchestrator-decision.schema.json` `nextWorkerInputs` — one item for two children. Every dispatch host fails the dispatch node `validation_error` and dispatches no child. | `failed` | ≤ 30s |
@@ -1012,6 +1015,22 @@ Unlike the other sub-directories, this one also carries **deliberately-invalid**
1012
1015
 
1013
1016
  ---
1014
1017
 
1018
+ ## Eval-suite fixtures (RFC 0244)
1019
+
1020
+ The `fixtures/eval-suites/` sub-directory holds `AgentEvalSuite` documents (`../schemas/v2/agent-eval-suite.schema.json`), validated server-free by `v2-eval-trajectory-trials.test.ts`. They are not workflows, so the top-level sweep never reads them. A host that runs them seeds each verbatim, lists its id (the file stem) in discovery `fixtures`, and resolves `evalSuiteRef: "https://conformance.openwop.dev/eval-suites/<id>"` to it.
1021
+
1022
+ Every task targets **the scripted agent `core.conformance.agents.scripted`**. Its contract:
1023
+
1024
+ - `input.script` is an ordered list of `{ tool, arguments? }`. The agent makes exactly those tool calls, in order, each emitting `agent.tool-called` with `agentId: "core.conformance.agents.scripted"`, `toolName: tool` and `inputs: arguments`.
1025
+ - Each call goes through the host's eval tool boundary. In a task with `fixtures.toolResponses`, the RFC 0244 §D selection and `onMiss` rules apply. Otherwise the host answers the call with its own `arguments`. No scripted call reaches a live tool or an external effect.
1026
+ - After the last call the agent decides, and its output is `input.output`.
1027
+
1028
+ | Fixture | Facet | What the suite asserts |
1029
+ | --- | --- | --- |
1030
+ | `conformance-eval-trajectory` | `agents.evalSuite.trajectory` | each task's `trajectory` counts equal `lib/eval-trajectory.ts`'s grade of the script; an unmatched trajectory fails; a `kind: "trajectory"` task scores 1 or 0; the log carries one `agent.tool-called` per scripted call |
1031
+ | `conformance-eval-trials` | `agents.evalSuite.trials` | `eval.started.trialCount: 3`; three `eval.scored` per task with `trialIndex` 0..2; the per-task `trials` block and `trialStats` equal the §B formulas. Without the facet: `400 validation_error` at `createRun` |
1032
+ | `conformance-eval-fixture-miss` | `agents.evalSuite.fixtureMatching` | `missed` reports `fixtureMissCount: 1` and `passed: false`; `served` passes with no miss. That no live tool ran is not observable here (RFC 0244 G2) |
1033
+
1015
1034
  ## OAuth provider fixtures
1016
1035
 
1017
1036
  RFC 0199 adds `provider.issuer`, `provider.pkce` and the two issuer-less providers (`issuerlessProviders[]`) the distinct-redirect-URI leg needs; the endpoints a host actually talks to are supplied at run time by the `oauth/authorize-start` seam, which points them at the suite's authorization-server double (`src/lib/oauth-as-double.ts`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.46.7",
3
+ "version": "2.46.8",
4
4
  "description": "Black-box conformance suite for OpenWOP hosts (v2 current; v1 scenarios retained for the frozen v1 tree).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -56,6 +56,6 @@
56
56
  "@openwop/spec-artifacts": "file:../spec-artifacts"
57
57
  },
58
58
  "peerDependencies": {
59
- "@openwop/spec-artifacts": "2.46.7"
59
+ "@openwop/spec-artifacts": "2.46.8"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -2,11 +2,11 @@
2
2
  "$comment": "GENERATED by conformance/scripts/generate-requirement-registry.mjs — do not edit. One record per it()/test() in src/scenarios. Ids: openwop.it.<file-stem>.<title-slug>[~n] (src/lib/requirement-ids.ts). A record with id null has an interpolated title; its run-time row is keyed by the rendered title and maps here by file+line only. Renamed ids need a row in requirement-aliases.json.",
3
3
  "generatedFrom": "src/scenarios/*.test.ts",
4
4
  "counts": {
5
- "files": 745,
6
- "tests": 2800,
7
- "withStableId": 2800,
5
+ "files": 746,
6
+ "tests": 2808,
7
+ "withStableId": 2808,
8
8
  "interpolatedTitles": 0,
9
- "explicitIds": 2673
9
+ "explicitIds": 2681
10
10
  },
11
11
  "records": [
12
12
  {
@@ -31025,6 +31025,210 @@
31025
31025
  }
31026
31026
  ]
31027
31027
  },
31028
+ {
31029
+ "id": "openwop.it.v2-eval-trajectory-trials.the-rfc-0244-suite-summary-and-event-fields-validate-and-the-negatives-are-refus",
31030
+ "file": "v2-eval-trajectory-trials.test.ts",
31031
+ "line": 113,
31032
+ "title": "the RFC 0244 suite, summary and event fields validate and the negatives are refused",
31033
+ "explicitId": "openwop.requirement.0244.wire-shape",
31034
+ "citations": [
31035
+ {
31036
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31037
+ "requirement": "trajectory.match \"superset\" MUST be refused (use any-order)"
31038
+ },
31039
+ {
31040
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31041
+ "requirement": "trials.count 0 MUST be refused"
31042
+ },
31043
+ {
31044
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31045
+ "requirement": "an unknown trajectory member MUST be refused"
31046
+ },
31047
+ {
31048
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31049
+ "requirement": null,
31050
+ "interpolated": true
31051
+ },
31052
+ {
31053
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31054
+ "requirement": "eval.started.trialCount MUST validate"
31055
+ },
31056
+ {
31057
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31058
+ "requirement": "an unknown evaluatorType MUST be refused"
31059
+ },
31060
+ {
31061
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31062
+ "requirement": null,
31063
+ "interpolated": true
31064
+ }
31065
+ ]
31066
+ },
31067
+ {
31068
+ "id": "openwop.it.v2-eval-trajectory-trials.the-rfc-0244-additions-are-content-free-eval-summary-no-content-leak",
31069
+ "file": "v2-eval-trajectory-trials.test.ts",
31070
+ "line": 136,
31071
+ "title": "the RFC 0244 additions are content-free (eval-summary-no-content-leak)",
31072
+ "explicitId": "openwop.requirement.0244.content-free",
31073
+ "citations": [
31074
+ {
31075
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31076
+ "requirement": "a trajectory block naming tools MUST be refused"
31077
+ },
31078
+ {
31079
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31080
+ "requirement": "a prose scoreLabel MUST be refused"
31081
+ }
31082
+ ]
31083
+ },
31084
+ {
31085
+ "id": "openwop.it.v2-eval-trajectory-trials.the-eval-suite-fixtures-validate-against-the-v2-suite-schema",
31086
+ "file": "v2-eval-trajectory-trials.test.ts",
31087
+ "line": 143,
31088
+ "title": "the eval-suite fixtures validate against the v2 suite schema",
31089
+ "explicitId": "openwop.requirement.0244.fixtures-valid",
31090
+ "citations": [
31091
+ {
31092
+ "section": "fixtures.md §eval-suites",
31093
+ "requirement": null,
31094
+ "interpolated": true
31095
+ }
31096
+ ]
31097
+ },
31098
+ {
31099
+ "id": "openwop.it.v2-eval-trajectory-trials.a-trajectory-suite-is-graded-per-the-reference-grader",
31100
+ "file": "v2-eval-trajectory-trials.test.ts",
31101
+ "line": 152,
31102
+ "title": "a trajectory suite is graded per the reference grader",
31103
+ "explicitId": "openwop.requirement.0244.trajectory-graded",
31104
+ "citations": [
31105
+ {
31106
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31107
+ "requirement": null,
31108
+ "interpolated": true
31109
+ },
31110
+ {
31111
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31112
+ "requirement": null,
31113
+ "interpolated": true
31114
+ },
31115
+ {
31116
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31117
+ "requirement": null,
31118
+ "interpolated": true
31119
+ },
31120
+ {
31121
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31122
+ "requirement": null,
31123
+ "interpolated": true
31124
+ },
31125
+ {
31126
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31127
+ "requirement": null,
31128
+ "interpolated": true
31129
+ }
31130
+ ]
31131
+ },
31132
+ {
31133
+ "id": "openwop.it.v2-eval-trajectory-trials.repeated-trials-emit-one-eval-scored-per-task-per-trial",
31134
+ "file": "v2-eval-trajectory-trials.test.ts",
31135
+ "line": 188,
31136
+ "title": "repeated trials emit one eval.scored per task per trial",
31137
+ "explicitId": "openwop.requirement.0244.trials-per-task",
31138
+ "citations": [
31139
+ {
31140
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31141
+ "requirement": "eval.started MUST carry trialCount"
31142
+ },
31143
+ {
31144
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31145
+ "requirement": null,
31146
+ "interpolated": true
31147
+ }
31148
+ ]
31149
+ },
31150
+ {
31151
+ "id": "openwop.it.v2-eval-trajectory-trials.the-per-task-trials-block-and-trialstats-follow-the-b-formulas",
31152
+ "file": "v2-eval-trajectory-trials.test.ts",
31153
+ "line": 199,
31154
+ "title": "the per-task trials block and trialStats follow the §B formulas",
31155
+ "explicitId": "openwop.requirement.0244.trial-stats-formula",
31156
+ "citations": [
31157
+ {
31158
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31159
+ "requirement": null,
31160
+ "interpolated": true
31161
+ },
31162
+ {
31163
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31164
+ "requirement": null,
31165
+ "interpolated": true
31166
+ },
31167
+ {
31168
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31169
+ "requirement": null,
31170
+ "interpolated": true
31171
+ },
31172
+ {
31173
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31174
+ "requirement": null,
31175
+ "interpolated": true
31176
+ },
31177
+ {
31178
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31179
+ "requirement": null,
31180
+ "interpolated": true
31181
+ },
31182
+ {
31183
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31184
+ "requirement": null,
31185
+ "interpolated": true
31186
+ }
31187
+ ]
31188
+ },
31189
+ {
31190
+ "id": "openwop.it.v2-eval-trajectory-trials.a-fixture-miss-under-onmiss-fail-fails-the-trial-and-is-counted",
31191
+ "file": "v2-eval-trajectory-trials.test.ts",
31192
+ "line": 222,
31193
+ "title": "a fixture miss under onMiss \"fail\" fails the trial and is counted",
31194
+ "explicitId": "openwop.requirement.0244.fixture-miss-fails",
31195
+ "citations": [
31196
+ {
31197
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31198
+ "requirement": "the missed task MUST report fixtureMissCount 1"
31199
+ },
31200
+ {
31201
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31202
+ "requirement": "a miss under onMiss \"fail\" MUST fail the trial"
31203
+ },
31204
+ {
31205
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31206
+ "requirement": "a served task MUST still pass"
31207
+ },
31208
+ {
31209
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31210
+ "requirement": "a served task MUST NOT report a miss"
31211
+ }
31212
+ ]
31213
+ },
31214
+ {
31215
+ "id": "openwop.it.v2-eval-trajectory-trials.a-suite-using-an-unadvertised-rfc-0244-feature-is-refused-at-createrun",
31216
+ "file": "v2-eval-trajectory-trials.test.ts",
31217
+ "line": 238,
31218
+ "title": "a suite using an unadvertised RFC 0244 feature is refused at createRun",
31219
+ "explicitId": "openwop.requirement.0244.unadvertised-feature-refused",
31220
+ "citations": [
31221
+ {
31222
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31223
+ "requirement": null,
31224
+ "interpolated": true
31225
+ },
31226
+ {
31227
+ "section": "agents.schema.json §evalSuite (RFC 0244)",
31228
+ "requirement": "the refusal MUST carry validation_error"
31229
+ }
31230
+ ]
31231
+ },
31028
31232
  {
31029
31233
  "id": "openwop.it.v2-event-type-closed.every-emitted-type-is-a-registered-protocol-type-or-a-vendor-type-under-an-adver",
31030
31234
  "file": "v2-event-type-closed.test.ts",
@@ -45856,7 +46060,7 @@
45856
46060
  {
45857
46061
  "id": "openwop.it.spec-corpus-validity.every-fixture-id-mentioned-in-fixtures-md-has-a-corresponding-json",
45858
46062
  "file": "spec-corpus-validity.test.ts",
45859
- "line": 1449,
46063
+ "line": 1453,
45860
46064
  "title": "every fixture id mentioned in fixtures.md has a corresponding JSON",
45861
46065
  "explicitId": "openwop.it.spec-corpus-validity.every-fixture-id-mentioned-in-fixtures-md-has-a-corresponding-json",
45862
46066
  "citations": [
@@ -45870,7 +46074,7 @@
45870
46074
  {
45871
46075
  "id": "openwop.it.spec-corpus-validity.every-fixture-json-file-is-referenced-by-fixtures-md",
45872
46076
  "file": "spec-corpus-validity.test.ts",
45873
- "line": 1491,
46077
+ "line": 1495,
45874
46078
  "title": "every fixture JSON file is referenced by fixtures.md",
45875
46079
  "explicitId": "openwop.it.spec-corpus-validity.every-fixture-json-file-is-referenced-by-fixtures-md",
45876
46080
  "citations": [
@@ -45884,7 +46088,7 @@
45884
46088
  {
45885
46089
  "id": "openwop.it.spec-corpus-validity.a-sample-bundle-validates-against-conformance-certification-bundle-schema-json",
45886
46090
  "file": "spec-corpus-validity.test.ts",
45887
- "line": 1544,
46091
+ "line": 1548,
45888
46092
  "title": "a sample bundle validates against conformance-certification-bundle.schema.json",
45889
46093
  "explicitId": "openwop.it.spec-corpus-validity.a-sample-bundle-validates-against-conformance-certification-bundle-schema-json",
45890
46094
  "citations": [
@@ -45898,7 +46102,7 @@
45898
46102
  {
45899
46103
  "id": "openwop.it.spec-corpus-validity.verifybundle-accepts-a-claim-that-is-discovery-derivable-and-floor-proven-b",
45900
46104
  "file": "spec-corpus-validity.test.ts",
45901
- "line": 1550,
46105
+ "line": 1554,
45902
46106
  "title": "verifyBundle ACCEPTS a claim that is discovery-derivable AND floor-proven (§B)",
45903
46107
  "explicitId": "openwop.it.spec-corpus-validity.verifybundle-accepts-a-claim-that-is-discovery-derivable-and-floor-proven-b",
45904
46108
  "citations": [
@@ -45911,7 +46115,7 @@
45911
46115
  {
45912
46116
  "id": "openwop.it.spec-corpus-validity.verifybundle-rejects-a-profile-its-discovery-document-does-not-derive-b-1",
45913
46117
  "file": "spec-corpus-validity.test.ts",
45914
- "line": 1557,
46118
+ "line": 1561,
45915
46119
  "title": "verifyBundle REJECTS a profile its discovery document does not derive (§B(1))",
45916
46120
  "explicitId": "openwop.it.spec-corpus-validity.verifybundle-rejects-a-profile-its-discovery-document-does-not-derive-b-1",
45917
46121
  "citations": [
@@ -45924,7 +46128,7 @@
45924
46128
  {
45925
46129
  "id": "openwop.it.spec-corpus-validity.verifybundle-rejects-a-bundle-missing-a-floor-scenario-b-2",
45926
46130
  "file": "spec-corpus-validity.test.ts",
45927
- "line": 1567,
46131
+ "line": 1571,
45928
46132
  "title": "verifyBundle REJECTS a bundle missing a floor scenario (§B(2))",
45929
46133
  "explicitId": "openwop.it.spec-corpus-validity.verifybundle-rejects-a-bundle-missing-a-floor-scenario-b-2",
45930
46134
  "citations": [
@@ -45937,7 +46141,7 @@
45937
46141
  {
45938
46142
  "id": "openwop.it.spec-corpus-validity.the-committed-bundle-file-exists-generated-by-certify",
45939
46143
  "file": "spec-corpus-validity.test.ts",
45940
- "line": 1604,
46144
+ "line": 1608,
45941
46145
  "title": "the committed bundle file exists (generated by --certify)",
45942
46146
  "explicitId": "openwop.it.spec-corpus-validity.the-committed-bundle-file-exists-generated-by-certify",
45943
46147
  "citations": [
@@ -45951,7 +46155,7 @@
45951
46155
  {
45952
46156
  "id": "openwop.it.spec-corpus-validity.the-committed-reference-bundle-validates-against-the-bundle-schema-a",
45953
46157
  "file": "spec-corpus-validity.test.ts",
45954
- "line": 1608,
46158
+ "line": 1612,
45955
46159
  "title": "the committed reference bundle validates against the bundle schema (§A)",
45956
46160
  "explicitId": "openwop.it.spec-corpus-validity.the-committed-reference-bundle-validates-against-the-bundle-schema-a",
45957
46161
  "citations": [
@@ -45965,7 +46169,7 @@
45965
46169
  {
45966
46170
  "id": "openwop.it.spec-corpus-validity.verifybundle-rejects-the-committed-reference-bundle-it-is-invalidated-rfc-0148-d",
45967
46171
  "file": "spec-corpus-validity.test.ts",
45968
- "line": 1615,
46172
+ "line": 1619,
45969
46173
  "title": "verifyBundle REJECTS the committed reference bundle — it is `invalidated` (RFC 0148 §D)",
45970
46174
  "explicitId": "openwop.it.spec-corpus-validity.verifybundle-rejects-the-committed-reference-bundle-it-is-invalidated-rfc-0148-d",
45971
46175
  "citations": [
@@ -45986,7 +46190,7 @@
45986
46190
  {
45987
46191
  "id": "openwop.it.spec-corpus-validity.discovery-sha256-is-the-canonical-json-sha-256-of-the-captured-discovery-documen",
45988
46192
  "file": "spec-corpus-validity.test.ts",
45989
- "line": 1641,
46193
+ "line": 1645,
45990
46194
  "title": "discovery.sha256 is the canonical-JSON SHA-256 of the captured discovery.document",
45991
46195
  "explicitId": "openwop.it.spec-corpus-validity.discovery-sha256-is-the-canonical-json-sha-256-of-the-captured-discovery-documen",
45992
46196
  "citations": [
@@ -45999,7 +46203,7 @@
45999
46203
  {
46000
46204
  "id": "openwop.it.spec-corpus-validity.the-requestbody-closes-at-the-composition-unevaluatedproperties-not-inside-a-bra",
46001
46205
  "file": "spec-corpus-validity.test.ts",
46002
- "line": 1702,
46206
+ "line": 1706,
46003
46207
  "title": "the requestBody closes at the composition (unevaluatedProperties), not inside a branch",
46004
46208
  "explicitId": "openwop.it.spec-corpus-validity.the-requestbody-closes-at-the-composition-unevaluatedproperties-not-inside-a-bra",
46005
46209
  "citations": [
@@ -46012,7 +46216,7 @@
46012
46216
  {
46013
46217
  "id": "openwop.it.spec-corpus-validity.canonical-createrun-bodies-pass-the-composed-schema-an-undeclared-property-fails",
46014
46218
  "file": "spec-corpus-validity.test.ts",
46015
- "line": 1717,
46219
+ "line": 1721,
46016
46220
  "title": "canonical createRun bodies PASS the composed schema; an undeclared property FAILS",
46017
46221
  "explicitId": "openwop.it.spec-corpus-validity.canonical-createrun-bodies-pass-the-composed-schema-an-undeclared-property-fails",
46018
46222
  "citations": [
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$comment": "GENERATED by conformance/scripts/generate-scenario-majors.mjs (RFC 0168 §D.3). Do not edit; add a file to BOTH_MAJORS in the generator to target both majors.",
3
3
  "counts": {
4
- "files": 657,
4
+ "files": 658,
5
5
  "v1": 455,
6
- "v2": 217
6
+ "v2": 218
7
7
  },
8
8
  "majors": {
9
9
  "a2a-1-0-agent-card.test.ts": [
@@ -1479,6 +1479,9 @@
1479
1479
  "v2-eval-mode-unadvertised-refused.test.ts": [
1480
1480
  2
1481
1481
  ],
1482
+ "v2-eval-trajectory-trials.test.ts": [
1483
+ 2
1484
+ ],
1482
1485
  "v2-event-type-closed.test.ts": [
1483
1486
  2
1484
1487
  ],
@@ -1,17 +1,17 @@
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.46.7",
5
- "corpusTag": "v2.46.7",
4
+ "version": "2.46.8",
5
+ "corpusTag": "v2.46.8",
6
6
  "files": {
7
7
  "api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
8
8
  "api/asyncapi.yaml": "d5ecb9ee6114582be3b1f662c84bfac9ae96dae7bacb853e461168f70a8e1c7d",
9
9
  "api/grpc/openwop.proto": "c3e72bb17cba514ee98feb6434e6c9b6ea6795bfd086489ec69fd882dd1ad977",
10
10
  "api/openapi.yaml": "ac6de5303872664b78ff419c035915593e01599e02835e777a0123de1baeb398",
11
11
  "api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
12
- "api/seams-v2.yaml": "1f51bb8566237c46f9df4d262f218667143e72664a56403838787360f0e543fb",
13
- "api/v2/asyncapi.yaml": "a4f623300eee3948f8debdf07cdf82c26ae7e6ceb9a6a15bd72a31adcd103690",
14
- "api/v2/openapi.yaml": "f1229644f8d1fa6d9e9b7e3ed83595e45daaf20ba4c8cad6b52fbdfa335b3575",
12
+ "api/seams-v2.yaml": "f9a0c7da940a9f31a689bea929c33b4e70b0293f955e2a91329ec46d86eced3f",
13
+ "api/v2/asyncapi.yaml": "77259d3d85bbc4cd83daa54382c7cfa08a84fb253cb0b1f7b8005a9a38d94ff7",
14
+ "api/v2/openapi.yaml": "139ef732f96270dc64e2365a42cdaa23f3177a80db63c9f16ee36f16935446f7",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "276fc1d51eb08e14e9127b9fd27761d7046ee33c12000f3a64a801322db8bf58",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -103,7 +103,7 @@
103
103
  "schemas/v2/a2ui-surface-delta-frame.schema.json": "6889c5da3fe7e76cb614038c0eea6774776fa640448558b258eb87fb50cb9b64",
104
104
  "schemas/v2/agent-deployment-transition.schema.json": "93a334ff042d51378c89fdd7134664271bf9592419bb5e3e00821ed06bc1904e",
105
105
  "schemas/v2/agent-deployment.schema.json": "d59b43fb62b522005ee22bc3bc53775a6777493e5e42d71812abc347625fa7fe",
106
- "schemas/v2/agent-eval-suite.schema.json": "b565c9af1fd665e11884e57525a581914deaa80a86e7a186a6c6623b207a55d0",
106
+ "schemas/v2/agent-eval-suite.schema.json": "e6925711c478f35c03dcc7d1c8b7c8ec0814d468ad1ed2beb3e54405d8eb0752",
107
107
  "schemas/v2/agent-inventory-response.schema.json": "5f74c3da946dfc3114624567a989717748081ab947b4d2af3f49034d67f4306b",
108
108
  "schemas/v2/agent-manifest.schema.json": "004ff150e955deebb9e64273de5093f5144b9b3895b09953e65aa74b61140778",
109
109
  "schemas/v2/agent-org-chart.schema.json": "113b079e1dce95908467cbef7f83e076fdb82aab5b0678fc1a43e32bfc9cbadf",
@@ -117,7 +117,7 @@
117
117
  "schemas/v2/artifact.schema.json": "85b9dc284d39b99d2a66fa8cbf47f2dd3d8526d3bd6cc411ad315b372de38975",
118
118
  "schemas/v2/audit-verify-result.schema.json": "14af8e9283510f0a40e48a2059c929ee44e0fc9fa3cc66a95bfbe90b17c48297",
119
119
  "schemas/v2/budget-policy.schema.json": "1de68095db0beb5c7a4be629de26e79f62ebbe748a88d46f36e768edfd1f0d68",
120
- "schemas/v2/capabilities.schema.json": "ac237b887fa6e3117eddf5568bb7c2a2664f491c140968b060a173e12e79d643",
120
+ "schemas/v2/capabilities.schema.json": "a717d34d98201cefe1769421c468d22e124d1a46881f60683cb6e103843dbc9a",
121
121
  "schemas/v2/certification-bundle.schema.json": "5cfa7fb5247160333e33a58b60d42bcd4e5269d8391d6a112d301e14828892ed",
122
122
  "schemas/v2/channel-presence-payload.schema.json": "fc03df0c20cc12a25f7ee6a47503a4c5931d79fe3d5ff24815e8fd7c1bcb9a87",
123
123
  "schemas/v2/channel-written-payload.schema.json": "ccecff3c71a3275ad8db035ac0e04db25e6f8d23cf091ddbcd5a60d1be1cbb6b",
@@ -145,7 +145,7 @@
145
145
  "schemas/v2/envelopes/schema.response.schema.json": "d049a081267c06c28879c331b147f3ed821b14ccfd93005c67aff64db0a8c68e",
146
146
  "schemas/v2/envelopes/ui.a2ui-surface.schema.json": "e92a2548ba65454c9e0a34f72ea6768d18f59e61fa5da7668d0b2d5baddaddc8",
147
147
  "schemas/v2/error-envelope.schema.json": "8ddb1d80330dfef17572a74dfe3a53e1a06411e4b0af6a6f1dd6e7e7461da987",
148
- "schemas/v2/eval-summary.schema.json": "2f384daf05b8f3b4f604688cae504d6d9971ba41b8c771e55fe9a17e09e86b7a",
148
+ "schemas/v2/eval-summary.schema.json": "fc8ea719a8241eacd12e90d36934369386673bd50edebe6814782a19605a7776",
149
149
  "schemas/v2/export-bundle.schema.json": "bd28905a7729437ab7f2ad8bb33e13a371b2bf5605a7eb2962a0d246de312d69",
150
150
  "schemas/v2/form-content-pack-manifest.schema.json": "a1942a124e250a6e9fbdb6b811c5a125e78fbcbcaa46dfc71d0636f24710a339",
151
151
  "schemas/v2/frontend-plugin-manifest.schema.json": "b279da288d542906a72b8cb4e872e001de632eaff539e0259b9cce287f82bf8f",
@@ -174,7 +174,7 @@
174
174
  "schemas/v2/residency.schema.json": "3fd23c9732beb140504e81560fc426eb6764d547e56da5fb92c5a5c7bf04db3a",
175
175
  "schemas/v2/run-ancestry-response.schema.json": "334c17f2062dea62726ca1d07b8be3f169cdc422af843c1def5fb317039814c9",
176
176
  "schemas/v2/run-diff-response.schema.json": "8bdb2045c274580038bd74781f7d062f5049839797c164a89de4ed89edc53a74",
177
- "schemas/v2/run-event-payloads.schema.json": "ff80204f4a9a55da482d8c7d54d6dbb8682e13fff155d4fc26fa5b7fa0790b83",
177
+ "schemas/v2/run-event-payloads.schema.json": "24992db063ccab827710d462e834cc6299bf6f7aa651b118e30ef33506cc7aae",
178
178
  "schemas/v2/run-event.schema.json": "2f0d7416bd376c56affe97b6b2b49981532e8e9ff45c0a162cf806c78b3e7fb7",
179
179
  "schemas/v2/run-list-response.schema.json": "c2dbc34768dcf64528914a213aa3cb55952e8e3e90a64b04a9423b2a19749d38",
180
180
  "schemas/v2/run-options.schema.json": "f0e9f494d80086c2ed2cf9fe87df54e5b4e66601699c4a1089f4edbc7ba58867",
@@ -208,14 +208,14 @@
208
208
  "schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
209
209
  "spec/v1/alias-detectors.json": "22444c22bb88c568339ef512553f9fc38146e22b566d11ab8608f655d688d19c",
210
210
  "spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
211
- "spec/v1/core-standard-manifest.json": "5c3946728d3bb64d4cf290e93156788445da37272d4e44a4da803be695f9b543",
211
+ "spec/v1/core-standard-manifest.json": "031419b7f2fa27bb1d550d5f8caf92cea889de73998aa723859c8a3e29b4458e",
212
212
  "spec/v1/deprecations.json": "e723bc11d7272c71789ad1303bb7f21809a8a024abf57594f9d5c35dc199ece9",
213
213
  "spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
214
214
  "spec/v1/end-of-support.json": "35f0bf3b70fe78b16e2a2fd81a153fc27e703f6c16376c6fe7cdf1f4f2c4e6d4",
215
215
  "spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
216
216
  "spec/v1/event-codemap.schema.json": "d05933b2e88103aff51a2f774df97b9bda065e0fc88dbd9b114cc47a32189174",
217
217
  "spec/v1/extensions.json": "79a60754aa16cbdbdb604f8e5c00038af535a1690a4d07bd5ff7c38f0256a7bc",
218
- "spec/v1/gaps.json": "e31dd0a2c440ff38469a8033e3324f372278ee94d209e0f84207d2111123f506",
218
+ "spec/v1/gaps.json": "a3ab3af1f48fe9f36283d296b06970a4dd759d4e3beb731828831f09e8e8f53b",
219
219
  "spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
220
220
  "spec/v1/migrations.json": "4c50760be70cb644d02427bcaf5749606700edf4513f423903cf3b1d12f5c300",
221
221
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
@@ -281,7 +281,7 @@
281
281
  "spec/v2/ext/sandbox-runtime-notes/README.md": "0d1290fb1e20b2e35c0b15051541ab6546e4e2a6f693b4f912cb3f7d6a65e92c",
282
282
  "spec/v2/ext/webResearch/README.md": "b675e6f6646128d781b661f754c2393eed86e5935d16c3a22df582a5ce025b9b",
283
283
  "spec/v2/facets/a2a.schema.json": "b68bf5e29e455e46c2ad7cfcccf5963730ec2310ff9db81884550d8df30fd6ef",
284
- "spec/v2/facets/agents.schema.json": "3be9326f7db9c7b9b4268aec731c0c2b087eba33c9bb31669ec75a18961bc40e",
284
+ "spec/v2/facets/agents.schema.json": "62a979d23219ca684bc16f91f81a4c92c2cb56e0f084d2e4f694c03e0c7125df",
285
285
  "spec/v2/facets/aiProviders.schema.json": "4eb911c2403b8f618325194855a1326680fbda2549dfbfe86a70dd2743f8fb12",
286
286
  "spec/v2/facets/auditLogIntegrity.schema.json": "9a7f81320c8f940c6f4129bc7dd895a4063231c5458a717a08c2cbf9bb886c46",
287
287
  "spec/v2/facets/auth.schema.json": "7d98bc81a0f7d307460d039eebfebd21388091bfe1d538762583940f94c0bd2a",
@@ -313,9 +313,9 @@
313
313
  "spec/v2/path-manifest.json": "7b5679a4dad98c80d855361d88a98c8a4e045af15e310862a05b285b815dc9fc",
314
314
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
315
315
  "spec/v2/profiles.json": "b382b261c7844798575f7c1b37c934f0da19846016a2337d883d4f5ef6e05479",
316
- "spec/v2/release.json": "1348d062c28c19897650cecb1f968c61adece3a61fdc4ecb8b7d8675468b7f65",
316
+ "spec/v2/release.json": "c291a28051bf090d15ad928487c427566b37bcded475b7475312200d91af022d",
317
317
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
318
- "spec/v2/surface-baseline.json": "185c226ce4868f6d435275b1005890c9328c18f2f86163f4622cce8b0dec63e3"
318
+ "spec/v2/surface-baseline.json": "15f53de22504577387afe746758f4bf5c6e76f1d6190cc1e5c9dd18ebd77e0b9"
319
319
  },
320
- "corpusCommit": "b7f55b6d06df56983be5a9e9d518dd5a8fae8b29"
320
+ "corpusCommit": "2793cd29bd41ca40727d919971b3ebc8b5e61dcf"
321
321
  }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * RFC 0244 — the suite's reference grader for eval trajectories and trials.
3
+ *
4
+ * Pure functions, no I/O. `v2-eval-trajectory-trials.test.ts` uses them to
5
+ * recompute what a host must report, so a host's grade is checked against the
6
+ * normative definition rather than against itself:
7
+ *
8
+ * gradeTrajectory §A — the four match modes, `forbidden`, `argsMatch`,
9
+ * and the mode-independent `matchedCount` (a maximum
10
+ * one-to-one matching, ignoring order).
11
+ * passAtK / passHatK / scoreStderr §B — the normative formulas.
12
+ *
13
+ * @see RFCS/0244-eval-trajectories-trials-and-recorded-fixtures.md
14
+ * @see schemas/v2/agent-eval-suite.schema.json, schemas/v2/eval-summary.schema.json
15
+ */
16
+
17
+ export type ArgsMatch = 'ignore' | 'exact' | 'subset';
18
+ export type MatchMode = 'exact' | 'in-order' | 'any-order' | 'subset';
19
+ export interface ExpectedCall { readonly tool: string; readonly args?: unknown; readonly argsMatch?: ArgsMatch }
20
+ export interface ObservedCall { readonly tool: string; readonly inputs?: unknown }
21
+ export interface TrajectorySpec { readonly match: MatchMode; readonly calls: readonly ExpectedCall[]; readonly forbidden?: readonly string[] }
22
+ export interface TrajectoryGrade {
23
+ readonly mode: MatchMode;
24
+ readonly matched: boolean;
25
+ readonly expectedCount: number;
26
+ readonly observedCount: number;
27
+ readonly matchedCount: number;
28
+ readonly forbiddenCount: number;
29
+ }
30
+
31
+ const isRecord = (v: unknown): v is Record<string, unknown> => v !== null && typeof v === 'object' && !Array.isArray(v);
32
+
33
+ /** JSON deep equality, key-order insensitive. */
34
+ export function jsonEqual(a: unknown, b: unknown): boolean {
35
+ if (a === b) return true;
36
+ if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((x, i) => jsonEqual(x, b[i]));
37
+ if (isRecord(a) && isRecord(b)) {
38
+ const ka = Object.keys(a);
39
+ return ka.length === Object.keys(b).length && ka.every((k) => k in b && jsonEqual(a[k], b[k]));
40
+ }
41
+ return false;
42
+ }
43
+
44
+ /** `subset`: every key path in `expected` is present in `actual` with a JSON-deep-equal value. */
45
+ export function jsonSubset(expected: unknown, actual: unknown): boolean {
46
+ if (isRecord(expected)) return isRecord(actual) && Object.keys(expected).every((k) => k in actual && jsonSubset(expected[k], actual[k]));
47
+ return jsonEqual(expected, actual);
48
+ }
49
+
50
+ export function callMatches(e: ExpectedCall, o: ObservedCall): boolean {
51
+ if (e.tool !== o.tool) return false;
52
+ const mode = e.argsMatch ?? 'ignore';
53
+ if (mode === 'ignore' || e.args === undefined) return true;
54
+ return mode === 'exact' ? jsonEqual(e.args, o.inputs) : jsonSubset(e.args, o.inputs);
55
+ }
56
+
57
+ /** Size of a maximum one-to-one matching (Kuhn's augmenting paths). */
58
+ function maxMatching(expected: readonly ExpectedCall[], observed: readonly ObservedCall[]): number {
59
+ const owner: (number | undefined)[] = new Array(observed.length).fill(undefined);
60
+ const augment = (i: number, seen: boolean[]): boolean => {
61
+ for (let j = 0; j < observed.length; j++) {
62
+ if (seen[j] || !callMatches(expected[i]!, observed[j]!)) continue;
63
+ seen[j] = true;
64
+ if (owner[j] === undefined || augment(owner[j]!, seen)) { owner[j] = i; return true; }
65
+ }
66
+ return false;
67
+ };
68
+ let n = 0;
69
+ for (let i = 0; i < expected.length; i++) if (augment(i, new Array(observed.length).fill(false))) n++;
70
+ return n;
71
+ }
72
+
73
+ function inOrder(expected: readonly ExpectedCall[], observed: readonly ObservedCall[]): boolean {
74
+ let j = 0;
75
+ for (const e of expected) {
76
+ while (j < observed.length && !callMatches(e, observed[j]!)) j++;
77
+ if (j === observed.length) return false;
78
+ j++;
79
+ }
80
+ return true;
81
+ }
82
+
83
+ export function gradeTrajectory(spec: TrajectorySpec, observed: readonly ObservedCall[]): TrajectoryGrade {
84
+ const forbidden = new Set(spec.forbidden ?? []);
85
+ const forbiddenCount = observed.filter((o) => forbidden.has(o.tool)).length;
86
+ const matchedCount = maxMatching(spec.calls, observed);
87
+ let modeOk: boolean;
88
+ switch (spec.match) {
89
+ case 'exact': modeOk = observed.length === spec.calls.length && spec.calls.every((e, i) => callMatches(e, observed[i]!)); break;
90
+ case 'in-order': modeOk = inOrder(spec.calls, observed); break;
91
+ case 'any-order': modeOk = matchedCount === spec.calls.length; break;
92
+ case 'subset': modeOk = observed.every((o) => spec.calls.some((e) => callMatches(e, o))); break;
93
+ }
94
+ return { mode: spec.match, matched: modeOk && forbiddenCount === 0, expectedCount: spec.calls.length, observedCount: observed.length, matchedCount, forbiddenCount };
95
+ }
96
+
97
+ /**
98
+ * C(a, k) / C(b, k) as an exact rational, converted to a double once. The
99
+ * ratio telescopes to Π (a−i)/(b−i) for i < k, so no factorial is formed and
100
+ * the result is stable for large n (pass^k(200, 100, 50) ≈ 2.2e-19).
101
+ */
102
+ function binomRatio(a: number, b: number, k: number): number {
103
+ if (k < 0 || k > b) throw new RangeError(`k=${k} outside 0..${b}`);
104
+ if (k > a) return 0;
105
+ let num = 1n;
106
+ let den = 1n;
107
+ for (let i = 0; i < k; i++) { num *= BigInt(a - i); den *= BigInt(b - i); }
108
+ return ratioToNumber(num, den);
109
+ }
110
+
111
+ /** num/den (num ≤ den, both > 0) to the nearest double, without overflowing either. */
112
+ function ratioToNumber(num: bigint, den: bigint): number {
113
+ if (num === 0n) return 0;
114
+ const shift = den.toString(2).length - num.toString(2).length + 64;
115
+ const q = (num << BigInt(shift)) / den;
116
+ let r = Number(q);
117
+ let e = shift;
118
+ while (e > 1000) { r *= 2 ** -1000; e -= 1000; }
119
+ return r * 2 ** -e;
120
+ }
121
+
122
+ /** pass@k = 1 − C(n−c, k) / C(n, k); 0 when c = 0. */
123
+ export function passAtK(n: number, c: number, k: number): number {
124
+ if (c === 0) return 0;
125
+ return 1 - binomRatio(n - c, n, k);
126
+ }
127
+
128
+ /** pass^k = C(c, k) / C(n, k); 0 when c < k. */
129
+ export function passHatK(n: number, c: number, k: number): number {
130
+ if (c < k) return 0;
131
+ return binomRatio(c, n, k);
132
+ }
133
+
134
+ /** RFC 0244 §B tolerance: a host value agrees with the formula when |host − ref| ≤ 1e-9 · max(1, |ref|). */
135
+ export function withinTolerance(host: number, ref: number): boolean {
136
+ return Number.isFinite(host) && Math.abs(host - ref) <= 1e-9 * Math.max(1, Math.abs(ref));
137
+ }
138
+
139
+ /** Sample sd of the per-task mean scores over √T; undefined when T < 2. */
140
+ export function scoreStderr(taskMeans: readonly number[]): number | undefined {
141
+ const t = taskMeans.length;
142
+ if (t < 2) return undefined;
143
+ const mean = taskMeans.reduce((a, b) => a + b, 0) / t;
144
+ const variance = taskMeans.reduce((a, b) => a + (b - mean) ** 2, 0) / (t - 1);
145
+ return Math.sqrt(variance) / Math.sqrt(t);
146
+ }
@@ -0,0 +1,253 @@
1
+ /**
2
+ * v2 — eval suites grade the tool path, run repeated trials, and fail closed
3
+ * on a fixture miss (RFC 0244; the rules are the `agents.evalSuite` facet
4
+ * description in `spec/v2/facets/agents.schema.json` and the
5
+ * `agent-eval-suite` / `eval-summary` / `run-event-payloads` descriptions).
6
+ *
7
+ * Server-free legs (always on):
8
+ * 1. The RFC 0244 suite, summary and `eval.scored` / `eval.started` fields
9
+ * validate, and the RFC's negatives are refused — including the
10
+ * content-free ones that are the v2 public test for SECURITY invariant
11
+ * `eval-summary-no-content-leak` (a trajectory block naming a tool, a
12
+ * prose `scoreLabel`).
13
+ * 2. The three eval-suite fixtures validate against the v2 suite schema.
14
+ *
15
+ * Host legs (gated on the facet member AND the fixture in discovery
16
+ * `fixtures`). Each fixture is an `AgentEvalSuite` under
17
+ * `fixtures/eval-suites/`, run against the scripted agent
18
+ * `core.conformance.agents.scripted` (contract in `fixtures.md`): every task's
19
+ * `input.script` is replayed as tool calls, so the trajectory is known to the
20
+ * suite in advance and the host's grade is checked against the reference
21
+ * grader in `lib/eval-trajectory.ts`, not against itself.
22
+ * 3. `conformance-eval-trajectory` — per-task `passed` and the trajectory
23
+ * counts equal the reference grade; the log carries the scripted calls.
24
+ * 4. `conformance-eval-trials` — `trialCount` on `eval.started`, one
25
+ * `eval.scored` per task per trial with contiguous `trialIndex`, and the
26
+ * per-task `trials` block and `trialStats` equal the §B formulas.
27
+ * 5. `conformance-eval-fixture-miss` — the missed task reports
28
+ * `fixtureMissCount: 1` and `passed: false`; the served one passes.
29
+ * That no live tool ran is seam-gated (RFC 0244 G2) and not asserted.
30
+ * 6. Refusal — a host advertising `agents.evalSuite` without `trials` that
31
+ * lists `conformance-eval-trials` refuses it at createRun with
32
+ * `400 validation_error`.
33
+ *
34
+ * @see RFCS/0244-eval-trajectories-trials-and-recorded-fixtures.md
35
+ */
36
+
37
+ import { describe, it, expect } from 'vitest';
38
+ import { readFileSync } from 'node:fs';
39
+ import { join } from 'node:path';
40
+ import { driver, type OpenWOPResponse } from '../lib/driver.js';
41
+ import { v2Discovery, v2Validator, v2RefValidator } from '../lib/v2.js';
42
+ import { softSkip } from '../lib/soft-skip.js';
43
+ import { req } from '../lib/requirement-ids.js';
44
+ import { readErrorCode } from '../lib/error-envelope.js';
45
+ import { majorProfile } from '../lib/major-profile.js';
46
+ import { isFixtureAdvertised } from '../lib/fixtures.js';
47
+ import { FIXTURES_DIR } from '../lib/paths.js';
48
+ import { pollTerminal, readEvents, isRecord, type Ev } from '../lib/fixture-run-observer.js';
49
+ import { gradeTrajectory, passAtK, passHatK, scoreStderr, withinTolerance, type TrajectorySpec } from '../lib/eval-trajectory.js';
50
+
51
+ const PROFILE = majorProfile(2);
52
+ const UNREADABLE = 'v2 discovery unreachable — /.well-known/openwop did not answer 200 JSON under OpenWOP-Version: 2.0';
53
+ const DOC = 'agents.schema.json §evalSuite (RFC 0244)';
54
+ const AGENT = 'core.conformance.agents.scripted';
55
+ const SUITE_URI = (id: string) => `https://conformance.openwop.dev/eval-suites/${id}`;
56
+ const ID_SHAPE = 'openwop.requirement.0244.wire-shape';
57
+ const ID_CONTENT_FREE = 'openwop.requirement.0244.content-free';
58
+ const ID_FIXTURES = 'openwop.requirement.0244.fixtures-valid';
59
+ const ID_TRAJECTORY = 'openwop.requirement.0244.trajectory-graded';
60
+ const ID_TRIALS = 'openwop.requirement.0244.trials-per-task';
61
+ const ID_TRIAL_STATS = 'openwop.requirement.0244.trial-stats-formula';
62
+ const ID_MISS = 'openwop.requirement.0244.fixture-miss-fails';
63
+ const ID_REFUSED = 'openwop.requirement.0244.unadvertised-feature-refused';
64
+
65
+ interface Task { taskId: string; input: { script: Array<{ tool: string; arguments?: unknown }>; output: string }; expected: { kind: string; trajectory?: TrajectorySpec } }
66
+ interface Suite { tasks: Task[]; trials?: { count: number; k?: number; passRule?: string } }
67
+ const fixture = (id: string): Suite => JSON.parse(readFileSync(join(FIXTURES_DIR, 'eval-suites', `${id}.json`), 'utf8')) as Suite;
68
+
69
+ const suiteSchema = v2Validator('agent-eval-suite');
70
+ const summarySchema = v2Validator('eval-summary');
71
+ const scored = v2RefValidator('run-event-payloads.schema.json#/$defs/evalScored');
72
+ const started = v2RefValidator('run-event-payloads.schema.json#/$defs/evalStarted');
73
+
74
+ async function http(fn: () => Promise<OpenWOPResponse>): Promise<OpenWOPResponse | null> {
75
+ try { return await fn(); } catch { return null; }
76
+ }
77
+
78
+ async function evalSuiteFacet(): Promise<{ doc: Record<string, unknown> | null; facet: Record<string, unknown> | null }> {
79
+ let doc: Record<string, unknown> | null;
80
+ try { doc = await v2Discovery(); } catch { doc = null; }
81
+ const agents = doc?.['agents'];
82
+ const facet = isRecord(agents) && isRecord(agents['evalSuite']) ? agents['evalSuite'] : null;
83
+ return { doc, facet };
84
+ }
85
+
86
+ type Observed = { kind: 'skip'; disposition: 'inapplicable' | 'blocked'; reason: string } | { kind: 'ok'; events: Ev[]; summary: Record<string, unknown> };
87
+
88
+ const runs = new Map<string, Promise<Observed>>();
89
+ /** One eval run per fixture per worker: two legs that read the same run share it. */
90
+ function runSuite(id: string): Promise<Observed> {
91
+ let p = runs.get(id);
92
+ if (p === undefined) { p = runSuiteOnce(id); runs.set(id, p); }
93
+ return p;
94
+ }
95
+
96
+ async function runSuiteOnce(id: string): Promise<Observed> {
97
+ const res = await http(() => driver.post(PROFILE.runsPath, { mode: 'eval', evalSuiteRef: SUITE_URI(id), agentId: AGENT }));
98
+ if (res === null) return { kind: 'skip', disposition: 'blocked', reason: 'POST /runs unreachable (fetch failed)' };
99
+ if (res.status === 429) return { kind: 'skip', disposition: 'blocked', reason: 'POST /runs answered 429 — the run budget, not the wire' };
100
+ const runId = isRecord(res.json) && typeof res.json['runId'] === 'string' ? res.json['runId'] : undefined;
101
+ if ((res.status !== 201 && res.status !== 202) || runId === undefined) return { kind: 'skip', disposition: 'blocked', reason: `createRun of eval suite ${id} answered ${res.status} ${readErrorCode(res.json) ?? ''}`.trim() };
102
+ const terminal = await pollTerminal(PROFILE, runId, { timeoutMs: 60_000 });
103
+ if (terminal.snapshot['status'] !== 'completed') return { kind: 'skip', disposition: 'blocked', reason: `eval run ${runId} ended ${String(terminal.snapshot['status'])}, not completed` };
104
+ const s = await http(() => driver.get(`${PROFILE.runsPath}/${PROFILE.idSegment(runId)}/eval-summary`));
105
+ if (s === null || s.status !== 200 || !isRecord(s.json)) return { kind: 'skip', disposition: 'blocked', reason: `GET …/eval-summary answered ${s?.status ?? 'nothing'}` };
106
+ return { kind: 'ok', events: await readEvents(PROFILE, runId), summary: s.json };
107
+ }
108
+
109
+ const taskEntry = (summary: Record<string, unknown>, taskId: string): Record<string, unknown> | undefined =>
110
+ (Array.isArray(summary['tasks']) ? (summary['tasks'] as unknown[]) : []).filter(isRecord).find((t) => t['taskId'] === taskId);
111
+
112
+ describe('v2 RFC 0244 — wire shape (server-free)', () => {
113
+ it('the RFC 0244 suite, summary and event fields validate and the negatives are refused', () => {
114
+ const traj = fixture('conformance-eval-trajectory');
115
+ const bad = (mut: (s: Suite) => void): boolean => { const s = structuredClone(traj) as Suite; mut(s); return suiteSchema(s).ok; };
116
+ expect(bad((s) => { (s.tasks[0]!.expected.trajectory as { match: string }).match = 'superset'; }), req(ID_SHAPE, DOC, 'trajectory.match "superset" MUST be refused (use any-order)')).toBe(false);
117
+ expect(bad((s) => { s.trials = { count: 0 }; }), req(ID_SHAPE, DOC, 'trials.count 0 MUST be refused')).toBe(false);
118
+ expect(bad((s) => { (s.tasks[0]!.expected.trajectory as unknown as Record<string, unknown>)['weight'] = 0.5; }), req(ID_SHAPE, DOC, 'an unknown trajectory member MUST be refused')).toBe(false);
119
+
120
+ const good = { taskId: 't', score: 1, passed: true, trialIndex: 2, scoreLabel: 'pass', evaluatorType: 'deterministic' };
121
+ expect(scored(good).ok, req(ID_SHAPE, DOC, `eval.scored with trialIndex/scoreLabel/evaluatorType MUST validate: ${scored(good).errors}`)).toBe(true);
122
+ expect(started({ suiteId: 'core.openwop.evals.x', suiteVersion: '1.0.0', taskCount: 2, modes: ['golden'], trialCount: 3 }).ok, req(ID_SHAPE, DOC, 'eval.started.trialCount MUST validate')).toBe(true);
123
+ expect(scored({ ...good, evaluatorType: 'gpt' }).ok, req(ID_SHAPE, DOC, 'an unknown evaluatorType MUST be refused')).toBe(false);
124
+
125
+ const summary = {
126
+ suiteId: 'core.openwop.evals.x', suiteVersion: '1.0.0', aggregateScore: 0.5, passed: false, taskCount: 1, passedCount: 0,
127
+ tasks: [{ taskId: 't', score: 0.8, passed: false,
128
+ trajectory: { mode: 'in-order', matched: false, expectedCount: 2, observedCount: 3, matchedCount: 1, forbiddenCount: 1 },
129
+ trials: { n: 5, passedCount: 4, meanScore: 0.8, passAtK: 1, passHatK: 0.4 },
130
+ efficiency: { toolCallCount: 3, modelCallCount: 1, inputTokens: 10, outputTokens: 5 }, fixtureMissCount: 0 }],
131
+ trialStats: { k: 3, passAtK: 1, passHatK: 0.4 },
132
+ };
133
+ expect(summarySchema(summary).ok, req(ID_SHAPE, DOC, `an RFC 0244 EvalSummary MUST validate: ${summarySchema(summary).errors}`)).toBe(true);
134
+ });
135
+
136
+ it('the RFC 0244 additions are content-free (eval-summary-no-content-leak)', () => {
137
+ const base = { suiteId: 'core.openwop.evals.x', suiteVersion: '1.0.0', aggregateScore: 0, passed: false, taskCount: 1, passedCount: 0 };
138
+ const leak = { ...base, tasks: [{ taskId: 't', score: 0, passed: false, trajectory: { mode: 'exact', matched: false, expectedCount: 1, observedCount: 1, matchedCount: 0, forbiddenCount: 1, tools: ['billing:refund'] } }] };
139
+ expect(summarySchema(leak).ok, req(ID_CONTENT_FREE, DOC, 'a trajectory block naming tools MUST be refused')).toBe(false);
140
+ expect(scored({ taskId: 't', score: 1, passed: true, scoreLabel: 'Looks good to me' }).ok, req(ID_CONTENT_FREE, DOC, 'a prose scoreLabel MUST be refused')).toBe(false);
141
+ });
142
+
143
+ it('the eval-suite fixtures validate against the v2 suite schema', () => {
144
+ for (const id of ['conformance-eval-trajectory', 'conformance-eval-trials', 'conformance-eval-fixture-miss']) {
145
+ const r = suiteSchema(fixture(id));
146
+ expect(r.ok, req(ID_FIXTURES, 'fixtures.md §eval-suites', `${id} MUST validate: ${r.errors}`)).toBe(true);
147
+ }
148
+ });
149
+ });
150
+
151
+ describe('v2 RFC 0244 — host behaviour', () => {
152
+ it('a trajectory suite is graded per the reference grader', async () => {
153
+ const { doc, facet } = await evalSuiteFacet();
154
+ if (!doc) return softSkip('blocked', UNREADABLE);
155
+ if (facet?.['trajectory'] !== true) return softSkip('inapplicable', 'the host does not advertise agents.evalSuite.trajectory');
156
+ const id = 'conformance-eval-trajectory';
157
+ if (!isFixtureAdvertised(id)) return softSkip('inapplicable', `the host does not advertise fixture ${id}`);
158
+ const o = await runSuite(id);
159
+ if (o.kind === 'skip') return softSkip(o.disposition, o.reason);
160
+ const suite = fixture(id);
161
+ for (const t of suite.tasks) {
162
+ const want = gradeTrajectory(t.expected.trajectory!, t.input.script.map((c) => ({ tool: c.tool, inputs: c.arguments })));
163
+ const entry = taskEntry(o.summary, t.taskId);
164
+ expect(entry, req(ID_TRAJECTORY, DOC, `the summary MUST carry task ${t.taskId}`)).toBeDefined();
165
+ const got = isRecord(entry?.['trajectory']) ? entry['trajectory'] : {};
166
+ expect(got, req(ID_TRAJECTORY, DOC, `task ${t.taskId}: the trajectory counts MUST equal the reference grade`)).toEqual(want);
167
+ if (!want.matched) expect(entry?.['passed'], req(ID_TRAJECTORY, DOC, `task ${t.taskId}: an unmatched trajectory MUST NOT pass`)).toBe(false);
168
+ if (t.expected.kind === 'trajectory') expect(entry?.['score'], req(ID_TRAJECTORY, DOC, `task ${t.taskId}: a trajectory-only task scores 1 when matched, else 0`)).toBe(want.matched ? 1 : 0);
169
+ }
170
+ const calls = o.events.filter((e) => e.type === 'agent.tool-called' && e.payload['agentId'] === AGENT).length;
171
+ const scripted = suite.tasks.reduce((a, t) => a + t.input.script.length, 0);
172
+ expect(calls, req(ID_TRAJECTORY, DOC, `the log MUST carry one agent.tool-called per scripted call (${scripted})`)).toBe(scripted);
173
+ });
174
+
175
+ async function trialsRun(): Promise<{ skip: [Parameters<typeof softSkip>[0], string] } | { o: Extract<Observed, { kind: 'ok' }>; suite: Suite; n: number; k: number }> {
176
+ const { doc, facet } = await evalSuiteFacet();
177
+ if (!doc) return { skip: ['blocked', UNREADABLE] };
178
+ if (!isRecord(facet?.['trials'])) return { skip: ['inapplicable', 'the host does not advertise agents.evalSuite.trials'] };
179
+ const id = 'conformance-eval-trials';
180
+ if (!isFixtureAdvertised(id)) return { skip: ['inapplicable', `the host does not advertise fixture ${id}`] };
181
+ const o = await runSuite(id);
182
+ if (o.kind === 'skip') return { skip: [o.disposition, o.reason] };
183
+ const suite = fixture(id);
184
+ const n = suite.trials!.count;
185
+ return { o, suite, n, k: suite.trials!.k ?? n };
186
+ }
187
+
188
+ it('repeated trials emit one eval.scored per task per trial', async () => {
189
+ const r = await trialsRun();
190
+ if ('skip' in r) return softSkip(...r.skip);
191
+ const { o, suite, n } = r;
192
+ expect(o.events.find((e) => e.type === 'eval.started')?.payload['trialCount'], req(ID_TRIALS, DOC, 'eval.started MUST carry trialCount')).toBe(n);
193
+ for (const t of suite.tasks) {
194
+ const rows = o.events.filter((e) => e.type === 'eval.scored' && e.payload['taskId'] === t.taskId);
195
+ expect(rows.map((x) => x.payload['trialIndex']).sort(), req(ID_TRIALS, DOC, `task ${t.taskId}: trialIndex MUST be 0..${n - 1}, once each`)).toEqual([...Array(n).keys()]);
196
+ }
197
+ });
198
+
199
+ it('the per-task trials block and trialStats follow the §B formulas', async () => {
200
+ const r = await trialsRun();
201
+ if ('skip' in r) return softSkip(...r.skip);
202
+ const { o, suite, n, k } = r;
203
+ const means: number[] = [];
204
+ for (const t of suite.tasks) {
205
+ const rows = o.events.filter((e) => e.type === 'eval.scored' && e.payload['taskId'] === t.taskId);
206
+ const c = rows.filter((x) => x.payload['passed'] === true).length;
207
+ const mean = rows.length === 0 ? 0 : rows.reduce((a, x) => a + Number(x.payload['score']), 0) / rows.length;
208
+ means.push(mean);
209
+ const trials = taskEntry(o.summary, t.taskId)?.['trials'];
210
+ const got = isRecord(trials) ? trials : {};
211
+ expect(got['n'], req(ID_TRIAL_STATS, DOC, `task ${t.taskId}: trials.n`)).toBe(n);
212
+ expect(got['passedCount'], req(ID_TRIAL_STATS, DOC, `task ${t.taskId}: trials.passedCount MUST count the passed trials`)).toBe(c);
213
+ expect(withinTolerance(Number(got['passAtK']), passAtK(n, c, k)), req(ID_TRIAL_STATS, DOC, `task ${t.taskId}: passAtK MUST equal 1 − C(n−c,k)/C(n,k) within 1e-9 relative (got ${String(got['passAtK'])}, want ${passAtK(n, c, k)})`)).toBe(true);
214
+ expect(withinTolerance(Number(got['passHatK']), passHatK(n, c, k)), req(ID_TRIAL_STATS, DOC, `task ${t.taskId}: passHatK MUST equal C(c,k)/C(n,k) within 1e-9 relative (got ${String(got['passHatK'])}, want ${passHatK(n, c, k)})`)).toBe(true);
215
+ expect(withinTolerance(Number(got['meanScore']), mean), req(ID_TRIAL_STATS, DOC, `task ${t.taskId}: meanScore MUST equal the mean trial score within 1e-9 relative (got ${String(got['meanScore'])}, want ${mean})`)).toBe(true);
216
+ }
217
+ const stats = isRecord(o.summary['trialStats']) ? o.summary['trialStats'] : {};
218
+ const se = scoreStderr(means);
219
+ if (se !== undefined) expect(withinTolerance(Number(stats['scoreStderr']), se), req(ID_TRIAL_STATS, DOC, `trialStats.scoreStderr MUST equal sd(per-task means)/√T within 1e-9 relative (got ${String(stats['scoreStderr'])}, want ${se})`)).toBe(true);
220
+ });
221
+
222
+ it('a fixture miss under onMiss "fail" fails the trial and is counted', async () => {
223
+ const { doc, facet } = await evalSuiteFacet();
224
+ if (!doc) return softSkip('blocked', UNREADABLE);
225
+ if (facet?.['fixtureMatching'] !== true) return softSkip('inapplicable', 'the host does not advertise agents.evalSuite.fixtureMatching');
226
+ const id = 'conformance-eval-fixture-miss';
227
+ if (!isFixtureAdvertised(id)) return softSkip('inapplicable', `the host does not advertise fixture ${id}`);
228
+ const o = await runSuite(id);
229
+ if (o.kind === 'skip') return softSkip(o.disposition, o.reason);
230
+ const missed = taskEntry(o.summary, 'missed');
231
+ const served = taskEntry(o.summary, 'served');
232
+ expect(missed?.['fixtureMissCount'], req(ID_MISS, DOC, 'the missed task MUST report fixtureMissCount 1')).toBe(1);
233
+ expect(missed?.['passed'], req(ID_MISS, DOC, 'a miss under onMiss "fail" MUST fail the trial')).toBe(false);
234
+ expect(served?.['passed'], req(ID_MISS, DOC, 'a served task MUST still pass')).toBe(true);
235
+ expect(served?.['fixtureMissCount'] ?? 0, req(ID_MISS, DOC, 'a served task MUST NOT report a miss')).toBe(0);
236
+ });
237
+
238
+ it('a suite using an unadvertised RFC 0244 feature is refused at createRun', async () => {
239
+ const { doc, facet } = await evalSuiteFacet();
240
+ if (!doc) return softSkip('blocked', UNREADABLE);
241
+ if (facet === null) return softSkip('inapplicable', 'the host does not advertise agents.evalSuite (v2-eval-mode-unadvertised-refused covers it)');
242
+ if (isRecord(facet['trials'])) return softSkip('inapplicable', 'the host advertises agents.evalSuite.trials, so the trials suite is served');
243
+ const id = 'conformance-eval-trials';
244
+ if (!isFixtureAdvertised(id)) return softSkip('inapplicable', `the host does not advertise fixture ${id}`);
245
+ const res = await http(() => driver.post(PROFILE.runsPath, { mode: 'eval', evalSuiteRef: SUITE_URI(id), agentId: AGENT }));
246
+ if (res === null) return softSkip('blocked', 'POST /runs unreachable (fetch failed)');
247
+ if (res.status === 429) return softSkip('blocked', 'POST /runs answered 429 — the run budget, not the wire');
248
+ const runId = isRecord(res.json) && typeof res.json['runId'] === 'string' ? res.json['runId'] : undefined;
249
+ if (res.status === 201 && runId !== undefined) await http(() => driver.post(`${PROFILE.runsPath}/${PROFILE.idSegment(runId)}/cancel`, {}));
250
+ expect(res.status, req(ID_REFUSED, DOC, `a trials suite on a host without agents.evalSuite.trials MUST be refused with 400 (got ${res.status})`)).toBe(400);
251
+ expect(readErrorCode(res.json), req(ID_REFUSED, DOC, 'the refusal MUST carry validation_error')).toBe('validation_error');
252
+ });
253
+ });