@openwop/openwop-conformance 2.46.15 → 2.46.17

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,17 @@
1
+ ## [2.46.17] — 2026-10-18 — RFC 0245: a recorded tool error replays as an error
2
+
3
+ - **New fixture `fixtures/eval-suites/conformance-eval-fixture-error`.** One task calls `conformance.eval:lookup` twice. Its fixtures serve an `isError: true` entry, then a success.
4
+ - **New legs in `v2-eval-trajectory-trials`:**
5
+ - `0245.fixture-error-shape` (server-free): an `isError` entry validates, and a string is refused.
6
+ - `0245.fixture-error-replayed`, gated on `fixtureErrors` + `fixtureMatching` and the fixture: the first call's `agent.tool-returned` carries `error {code, message}` and no `outcome` (`status: "error"` under tool hooks), the second carries `outcome`, and `fixtureMissCount` is 0.
7
+ - `0245.unadvertised-refused`: without `fixtureErrors`, the suite is refused with `400 validation_error`.
8
+ The judge is `lib/eval-fixture-error-witness.ts`; its self-tests fail each wrong host answer.
9
+ - **Host impact: none today.** No host advertises `fixtureErrors`, so the replay leg records `inapplicable`. The refusal leg binds on a host that lists the fixture and advertises `evalSuite` without the member.
10
+
11
+ ## [2.46.16] — 2026-10-18 — RFC 0228 Accepted: gap G4 closes
12
+
13
+ - **No assertion changes.** The packed `spec/v1/gaps.json` changes: RFC 0228 G4 (a host witness of `v2-table-schema-enforcement`) closes on openwop-app's certified 2.46.14 production cut (`ddcf7f944`), and RFC 0228 goes `Accepted`.
14
+
1
15
  ## [2.46.15] — 2026-10-17 — RFC 0228: gap G7 closes
2
16
 
3
17
  - **No assertion changes.** The packed `spec/v1/gaps.json` changes: RFC 0228 G7 (MyndHyve's run-creation `validation_error` leg) closes on MyndHyve's certified 2.46.13 cut (`c8d511d84`), which contains myndhyve#597, with a live probe of that build. Every RFC 0228 rename row is now closed.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "package": "@openwop/spec-artifacts",
3
- "version": "2.46.15",
4
- "stampSha256": "6208e336d886b7202d3cf537bf09c85e89c4384e0d31ed133722abc4c6314fdc"
3
+ "version": "2.46.17",
4
+ "stampSha256": "d8df0298a5a6405eca16d0a24cb7c48123d7e3e0675b337eca37ad36585e02f6"
5
5
  }
@@ -0,0 +1,68 @@
1
+ {
2
+ "suiteId": "core.openwop.evals.conformance-eval-fixture-error",
3
+ "version": "1.0.0",
4
+ "targetAgentId": "core.conformance.agents.scripted",
5
+ "modes": [
6
+ "golden"
7
+ ],
8
+ "tasks": [
9
+ {
10
+ "taskId": "recovers",
11
+ "input": {
12
+ "script": [
13
+ {
14
+ "tool": "conformance.eval:lookup",
15
+ "arguments": {
16
+ "orderId": "o-1"
17
+ }
18
+ },
19
+ {
20
+ "tool": "conformance.eval:lookup",
21
+ "arguments": {
22
+ "orderId": "o-1"
23
+ }
24
+ }
25
+ ],
26
+ "output": "done"
27
+ },
28
+ "expected": {
29
+ "kind": "golden",
30
+ "match": {
31
+ "strategy": "contains",
32
+ "value": "done"
33
+ }
34
+ },
35
+ "fixtures": {
36
+ "onMiss": "fail",
37
+ "toolResponses": [
38
+ {
39
+ "tool": "conformance.eval:lookup",
40
+ "args": {
41
+ "orderId": "o-1"
42
+ },
43
+ "argsMatch": "exact",
44
+ "isError": true,
45
+ "response": {
46
+ "content": [
47
+ {
48
+ "type": "text",
49
+ "text": "{\"error\":\"upstream_down\"}"
50
+ }
51
+ ]
52
+ }
53
+ },
54
+ {
55
+ "tool": "conformance.eval:lookup",
56
+ "args": {
57
+ "orderId": "o-1"
58
+ },
59
+ "argsMatch": "exact",
60
+ "response": {
61
+ "status": "shipped"
62
+ }
63
+ }
64
+ ]
65
+ }
66
+ }
67
+ ]
68
+ }
package/fixtures.md CHANGED
@@ -74,6 +74,7 @@ All fixtures MUST advertise:
74
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 |
75
75
  | Eval — Trajectory Gate | `conformance-eval-trajectory-gate` | RFC 0246 — two golden tasks scoring 1 at `passScore: 0.6`; `path-matched` keeps its trajectory, `path-wrong` calls a forbidden tool (`v2-eval-trajectory-trials`) | eval run `completed`; `aggregateScore` 1, `gatedAggregateScore` 0.5, suite `passed: false` | ≤ 30s |
76
76
  | Eval — Trajectory Gate (mean) | `conformance-eval-trajectory-gate-mean` | RFC 0246 — `trials: {count: 2, passRule: "mean"}`, one golden task scoring 1 that calls a forbidden tool in both trials (`v2-eval-trajectory-trials`) | eval run `completed`; `meanScore` 1, `gatedScore` 0, task `passed: false` | ≤ 30s |
77
+ | Eval — Fixture Error | `conformance-eval-fixture-error` | RFC 0245 — `onMiss: "fail"`; one task calls the same tool twice, served first by an `isError: true` entry, then by a success (`v2-eval-trajectory-trials`) | eval run `completed`; the first call is recorded as a tool error | ≤ 30s |
77
78
  | 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 |
78
79
  | 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 |
79
80
  | 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 |
@@ -1034,6 +1035,7 @@ Every task targets **the scripted agent `core.conformance.agents.scripted`**. It
1034
1035
  | `conformance-eval-fixture-miss` | `agents.evalSuite.fixtureMatching` | `missed` reports `fixtureMissCount: 1`, `passed: false` and `score: 0` (summary and every `eval.scored`); `served` passes with no miss. That no live tool ran is not observable here (RFC 0244 G2) |
1035
1036
  | `conformance-eval-trajectory-gate` | `agents.evalSuite.trajectory` | RFC 0246: both tasks keep `score` 1 and `aggregateScore` 1; `path-matched` carries `gatedScore` 1 and `path-wrong` `gatedScore` 0; `gatedAggregateScore` 0.5; suite `passed: false` although `aggregateScore` clears `passScore` 0.6 |
1036
1037
  | `conformance-eval-trajectory-gate-mean` | `agents.evalSuite.trajectory` + `agents.evalSuite.trials` | RFC 0246: two `eval.scored` with `score` 1 and `passed: false`; `trials.meanScore` 1, `gatedScore` 0, task `passed: false`; the summary carries `gatedAggregateScore` |
1038
+ | `conformance-eval-fixture-error` | `agents.evalSuite.fixtureErrors` + `agents.evalSuite.fixtureMatching` | RFC 0245: two `agent.tool-called` and two `agent.tool-returned` for `conformance.eval:lookup`, paired by `callId`; the first return carries `error` with non-empty `code` and `message` and no `outcome` (`status: "error"` under `toolHooks.prePostEvents`), the second carries `outcome`; `recovers` reports `fixtureMissCount: 0`. Without `fixtureErrors`: `400 validation_error`. That the agent saw an error and no live tool ran is not observable here (RFC 0245 G1, RFC 0244 G2) |
1037
1039
 
1038
1040
  ## OAuth provider fixtures
1039
1041
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "2.46.15",
3
+ "version": "2.46.17",
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.15"
59
+ "@openwop/spec-artifacts": "2.46.17"
60
60
  }
61
61
  }
package/requirements.json CHANGED
@@ -3,10 +3,10 @@
3
3
  "generatedFrom": "src/scenarios/*.test.ts",
4
4
  "counts": {
5
5
  "files": 746,
6
- "tests": 2811,
7
- "withStableId": 2811,
6
+ "tests": 2814,
7
+ "withStableId": 2814,
8
8
  "interpolatedTitles": 0,
9
- "explicitIds": 2684
9
+ "explicitIds": 2687
10
10
  },
11
11
  "records": [
12
12
  {
@@ -31028,7 +31028,7 @@
31028
31028
  {
31029
31029
  "id": "openwop.it.v2-eval-trajectory-trials.the-rfc-0244-suite-summary-and-event-fields-validate-and-the-negatives-are-refus",
31030
31030
  "file": "v2-eval-trajectory-trials.test.ts",
31031
- "line": 132,
31031
+ "line": 150,
31032
31032
  "title": "the RFC 0244 suite, summary and event fields validate and the negatives are refused",
31033
31033
  "explicitId": "openwop.requirement.0244.wire-shape",
31034
31034
  "citations": [
@@ -31067,7 +31067,7 @@
31067
31067
  {
31068
31068
  "id": "openwop.it.v2-eval-trajectory-trials.the-rfc-0244-additions-are-content-free-eval-summary-no-content-leak",
31069
31069
  "file": "v2-eval-trajectory-trials.test.ts",
31070
- "line": 155,
31070
+ "line": 173,
31071
31071
  "title": "the RFC 0244 additions are content-free (eval-summary-no-content-leak)",
31072
31072
  "explicitId": "openwop.requirement.0244.content-free",
31073
31073
  "citations": [
@@ -31084,7 +31084,7 @@
31084
31084
  {
31085
31085
  "id": "openwop.it.v2-eval-trajectory-trials.the-eval-suite-fixtures-validate-against-the-v2-suite-schema",
31086
31086
  "file": "v2-eval-trajectory-trials.test.ts",
31087
- "line": 162,
31087
+ "line": 180,
31088
31088
  "title": "the eval-suite fixtures validate against the v2 suite schema",
31089
31089
  "explicitId": "openwop.requirement.0244.fixtures-valid",
31090
31090
  "citations": [
@@ -31098,7 +31098,7 @@
31098
31098
  {
31099
31099
  "id": "openwop.it.v2-eval-trajectory-trials.the-rfc-0246-gating-fields-validate-and-stay-in-0-1",
31100
31100
  "file": "v2-eval-trajectory-trials.test.ts",
31101
- "line": 169,
31101
+ "line": 187,
31102
31102
  "title": "the RFC 0246 gating fields validate and stay in 0–1",
31103
31103
  "explicitId": "openwop.requirement.0246.gated-fields-shape",
31104
31104
  "citations": [
@@ -31117,10 +31117,32 @@
31117
31117
  }
31118
31118
  ]
31119
31119
  },
31120
+ {
31121
+ "id": "openwop.it.v2-eval-trajectory-trials.a-fixture-entry-may-be-marked-iserror-and-only-as-a-boolean-rfc-0245-a",
31122
+ "file": "v2-eval-trajectory-trials.test.ts",
31123
+ "line": 195,
31124
+ "title": "a fixture entry may be marked isError, and only as a boolean (RFC 0245 §A)",
31125
+ "explicitId": "openwop.requirement.0245.fixture-error-shape",
31126
+ "citations": [
31127
+ {
31128
+ "section": "agents.schema.json §evalSuite.fixtureErrors (RFC 0245)",
31129
+ "requirement": null,
31130
+ "interpolated": true
31131
+ },
31132
+ {
31133
+ "section": "agents.schema.json §evalSuite.fixtureErrors (RFC 0245)",
31134
+ "requirement": "an isError: false entry MUST validate"
31135
+ },
31136
+ {
31137
+ "section": "agents.schema.json §evalSuite.fixtureErrors (RFC 0245)",
31138
+ "requirement": "a string isError MUST be refused"
31139
+ }
31140
+ ]
31141
+ },
31120
31142
  {
31121
31143
  "id": "openwop.it.v2-eval-trajectory-trials.a-trajectory-suite-is-graded-per-the-reference-grader",
31122
31144
  "file": "v2-eval-trajectory-trials.test.ts",
31123
- "line": 179,
31145
+ "line": 205,
31124
31146
  "title": "a trajectory suite is graded per the reference grader",
31125
31147
  "explicitId": "openwop.requirement.0244.trajectory-graded",
31126
31148
  "citations": [
@@ -31154,7 +31176,7 @@
31154
31176
  {
31155
31177
  "id": "openwop.it.v2-eval-trajectory-trials.repeated-trials-emit-one-eval-scored-per-task-per-trial",
31156
31178
  "file": "v2-eval-trajectory-trials.test.ts",
31157
- "line": 215,
31179
+ "line": 241,
31158
31180
  "title": "repeated trials emit one eval.scored per task per trial",
31159
31181
  "explicitId": "openwop.requirement.0244.trials-per-task",
31160
31182
  "citations": [
@@ -31172,7 +31194,7 @@
31172
31194
  {
31173
31195
  "id": "openwop.it.v2-eval-trajectory-trials.the-per-task-trials-block-and-trialstats-follow-the-b-formulas",
31174
31196
  "file": "v2-eval-trajectory-trials.test.ts",
31175
- "line": 226,
31197
+ "line": 252,
31176
31198
  "title": "the per-task trials block and trialStats follow the §B formulas",
31177
31199
  "explicitId": "openwop.requirement.0244.trial-stats-formula",
31178
31200
  "citations": [
@@ -31211,7 +31233,7 @@
31211
31233
  {
31212
31234
  "id": "openwop.it.v2-eval-trajectory-trials.a-fixture-miss-under-onmiss-fail-fails-the-trial-and-is-counted",
31213
31235
  "file": "v2-eval-trajectory-trials.test.ts",
31214
- "line": 249,
31236
+ "line": 275,
31215
31237
  "title": "a fixture miss under onMiss \"fail\" fails the trial and is counted",
31216
31238
  "explicitId": "openwop.requirement.0244.fixture-miss-fails",
31217
31239
  "citations": [
@@ -31248,7 +31270,7 @@
31248
31270
  {
31249
31271
  "id": "openwop.it.v2-eval-trajectory-trials.a-suite-using-an-unadvertised-rfc-0244-feature-is-refused-at-createrun",
31250
31272
  "file": "v2-eval-trajectory-trials.test.ts",
31251
- "line": 269,
31273
+ "line": 295,
31252
31274
  "title": "a suite using an unadvertised RFC 0244 feature is refused at createRun",
31253
31275
  "explicitId": "openwop.requirement.0244.unadvertised-feature-refused",
31254
31276
  "citations": [
@@ -31266,7 +31288,7 @@
31266
31288
  {
31267
31289
  "id": "openwop.it.v2-eval-trajectory-trials.under-passrule-mean-a-task-whose-trials-failed-their-trajectory-does-not-pass-rf",
31268
31290
  "file": "v2-eval-trajectory-trials.test.ts",
31269
- "line": 285,
31291
+ "line": 311,
31270
31292
  "title": "under passRule \"mean\" a task whose trials failed their trajectory does not pass (RFC 0246)",
31271
31293
  "explicitId": "openwop.requirement.0246.mean-gated",
31272
31294
  "citations": [
@@ -31280,7 +31302,7 @@
31280
31302
  {
31281
31303
  "id": "openwop.it.v2-eval-trajectory-trials.a-suite-whose-tasks-failed-their-trajectory-does-not-pass-on-the-output-scores-r",
31282
31304
  "file": "v2-eval-trajectory-trials.test.ts",
31283
- "line": 298,
31305
+ "line": 324,
31284
31306
  "title": "a suite whose tasks failed their trajectory does not pass on the output scores (RFC 0246)",
31285
31307
  "explicitId": "openwop.requirement.0246.suite-gated",
31286
31308
  "citations": [
@@ -31291,6 +31313,38 @@
31291
31313
  }
31292
31314
  ]
31293
31315
  },
31316
+ {
31317
+ "id": "openwop.it.v2-eval-trajectory-trials.a-fixture-entry-marked-iserror-is-replayed-as-a-tool-error-not-a-success-rfc-024",
31318
+ "file": "v2-eval-trajectory-trials.test.ts",
31319
+ "line": 335,
31320
+ "title": "a fixture entry marked isError is replayed as a tool error, not a success (RFC 0245)",
31321
+ "explicitId": "openwop.requirement.0245.fixture-error-replayed",
31322
+ "citations": [
31323
+ {
31324
+ "section": null,
31325
+ "requirement": null,
31326
+ "interpolated": true
31327
+ }
31328
+ ]
31329
+ },
31330
+ {
31331
+ "id": "openwop.it.v2-eval-trajectory-trials.a-suite-using-iserror-is-refused-at-createrun-without-fixtureerrors-rfc-0245-c",
31332
+ "file": "v2-eval-trajectory-trials.test.ts",
31333
+ "line": 349,
31334
+ "title": "a suite using isError is refused at createRun without fixtureErrors (RFC 0245 §C)",
31335
+ "explicitId": "openwop.requirement.0245.unadvertised-refused",
31336
+ "citations": [
31337
+ {
31338
+ "section": "agents.schema.json §evalSuite.fixtureErrors (RFC 0245)",
31339
+ "requirement": null,
31340
+ "interpolated": true
31341
+ },
31342
+ {
31343
+ "section": "agents.schema.json §evalSuite.fixtureErrors (RFC 0245)",
31344
+ "requirement": "the refusal MUST carry validation_error"
31345
+ }
31346
+ ]
31347
+ },
31294
31348
  {
31295
31349
  "id": "openwop.it.v2-event-type-closed.every-emitted-type-is-a-registered-protocol-type-or-a-vendor-type-under-an-adver",
31296
31350
  "file": "v2-event-type-closed.test.ts",
@@ -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.15",
5
- "corpusTag": "v2.46.15",
4
+ "version": "2.46.17",
5
+ "corpusTag": "v2.46.17",
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": "b65af486352308cc3fddb032c8318e8bf58757e5bf6111c3ae70906944c0018b",
13
- "api/v2/asyncapi.yaml": "706f386c6db7665d69468694af6f0de452f51052b811b34bd3140accfd7b72f3",
14
- "api/v2/openapi.yaml": "5c0e15d6db838252435b61411a03d283bb3a42e53b6a9929825d1c2666756865",
12
+ "api/seams-v2.yaml": "08c520d1ccc23d43054ee5499276d73e18f3350d0d8825d5caa3934286e7af19",
13
+ "api/v2/asyncapi.yaml": "4625ddc33db126fd959e48084edca58121340042a5e1e170f87a9b4851b8b2d8",
14
+ "api/v2/openapi.yaml": "993bda2b91c365f8b3751fd3c827de538e39ae8e4ff3fefe2452b7b261b3c110",
15
15
  "api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
16
16
  "schemas/README.md": "276fc1d51eb08e14e9127b9fd27761d7046ee33c12000f3a64a801322db8bf58",
17
17
  "schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
@@ -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": "b4c09ddeab1c5bca702340cc28e7ec938fe26ed1f9537e7c24b4658fc719101d",
211
+ "spec/v1/core-standard-manifest.json": "d8a41123025c45515092970fae945b4cfa238d550cdfb9549cb649e740e8b7e3",
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": "518cdd55e5e9fcf56157ab549f00e9990be2a416698d09310c113f26b2b676f9",
218
+ "spec/v1/gaps.json": "572b68c8a4926339fb94017fcd8f9c78adfaa4900dc809fb8e76b094a7260ba0",
219
219
  "spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
220
220
  "spec/v1/migrations.json": "4c50760be70cb644d02427bcaf5749606700edf4513f423903cf3b1d12f5c300",
221
221
  "spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
@@ -314,9 +314,9 @@
314
314
  "spec/v2/path-manifest.json": "7b5679a4dad98c80d855361d88a98c8a4e045af15e310862a05b285b815dc9fc",
315
315
  "spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
316
316
  "spec/v2/profiles.json": "b382b261c7844798575f7c1b37c934f0da19846016a2337d883d4f5ef6e05479",
317
- "spec/v2/release.json": "e198402d77d27c62f19c9c7c0a66d18e64df2616a5c16243d6005650522ff8a5",
317
+ "spec/v2/release.json": "b6e1227e823697bad80029172057d535b866ca0d8faac6a1254ecfa9074b3654",
318
318
  "spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
319
- "spec/v2/surface-baseline.json": "9931a6fce192bb1eb427ddf75b53fcea6f5d3b775e40e4c9f1778d4370ba09c1"
319
+ "spec/v2/surface-baseline.json": "60061090d890cf22d91de7a4515e95814bde5690f9f9f843ee7efbdf0cdb1712"
320
320
  },
321
- "corpusCommit": "e34b1c09bf6878c8be3a61ce4db06c4b2ddf8664"
321
+ "corpusCommit": "3d19f1af09cc0499a1bdfd35dac28707b16806a9"
322
322
  }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * RFC 0245 — an eval fixture entry marked `isError: true` is replayed as a
3
+ * tool error, never as a success. A pure judge over one eval run of the
4
+ * `conformance-eval-fixture-error` fixture, for
5
+ * `v2-eval-trajectory-trials.test.ts`.
6
+ *
7
+ * The fixture's one task scripts the same call twice, and its fixtures serve
8
+ * an `isError: true` entry and then a success (RFC 0244 §D: first unconsumed
9
+ * match). So the expected log is known in advance: two `agent.tool-called`
10
+ * from the scripted agent, and two `agent.tool-returned` paired to them by
11
+ * `callId` — the first with `error` and no `outcome`, the second with
12
+ * `outcome`. A served error is not a miss, so `fixtureMissCount` stays 0.
13
+ *
14
+ * That the agent saw a tool error, and that no live tool ran, are seam-gated
15
+ * (RFC 0245 G1, RFC 0244 G2) and not judged here.
16
+ *
17
+ * @see RFCS/0245-eval-fixtures-replay-tool-errors.md
18
+ */
19
+
20
+ export interface FixtureErrorFinding { readonly ok: boolean; readonly doc: string; readonly message: string }
21
+
22
+ type Rec = Record<string, unknown>;
23
+ interface Event { readonly type: string; readonly sequence: number; readonly payload: Rec }
24
+
25
+ const isRec = (v: unknown): v is Rec => typeof v === 'object' && v !== null && !Array.isArray(v);
26
+ const nonEmpty = (v: unknown): boolean => typeof v === 'string' && v.length > 0;
27
+ const f = (ok: boolean, doc: string, message: string): FixtureErrorFinding => ({ ok, doc, message });
28
+
29
+ const B = 'RFC 0245 §B';
30
+ export const FIXTURE_ERROR_TOOL = 'conformance.eval:lookup';
31
+ export const FIXTURE_ERROR_TASK = 'recovers';
32
+
33
+ export interface FixtureErrorOptions {
34
+ /** The scripted agent's id; tool events from any other agent are ignored. */
35
+ readonly agentId: string;
36
+ /** Discovery `toolHooks.prePostEvents === true`: the error return also carries `status: "error"`. */
37
+ readonly prePostEvents: boolean;
38
+ }
39
+
40
+ export function judgeFixtureErrorReplayed(events: readonly Event[], summary: Rec, opts: FixtureErrorOptions): FixtureErrorFinding[] {
41
+ const mine = (e: Event): boolean => e.payload['agentId'] === opts.agentId && e.payload['toolName'] === FIXTURE_ERROR_TOOL;
42
+ const ordered = [...events].sort((a, b) => a.sequence - b.sequence);
43
+ const called = ordered.filter((e) => e.type === 'agent.tool-called' && mine(e));
44
+ const returned = ordered.filter((e) => e.type === 'agent.tool-returned' && mine(e));
45
+ const out: FixtureErrorFinding[] = [];
46
+
47
+ out.push(f(called.length === 2, B, `the log MUST carry two agent.tool-called for ${FIXTURE_ERROR_TOOL} (got ${called.length})`));
48
+ out.push(f(returned.length === 2, B, `the log MUST carry two agent.tool-returned for ${FIXTURE_ERROR_TOOL} (got ${returned.length})`));
49
+
50
+ const byCall = (c: Event | undefined): Rec | undefined => {
51
+ const id = c?.payload['callId'];
52
+ if (!nonEmpty(id)) return undefined;
53
+ return returned.find((r) => r.payload['callId'] === id)?.payload;
54
+ };
55
+ const first = byCall(called[0]);
56
+ const second = byCall(called[1]);
57
+
58
+ if (first === undefined) {
59
+ out.push(f(false, B, 'the first scripted call MUST have an agent.tool-returned with the same callId'));
60
+ } else {
61
+ const err = first['error'];
62
+ out.push(f(isRec(err) && nonEmpty(err['code']) && nonEmpty(err['message']), B, 'the call served by the isError entry MUST be recorded with error {code, message}, both non-empty'));
63
+ out.push(f(!('outcome' in first) || first['outcome'] === undefined, B, 'the call served by the isError entry MUST NOT carry outcome — that replays a failure as a success'));
64
+ if (opts.prePostEvents) out.push(f(first['status'] === 'error', B, `under toolHooks.prePostEvents the error return MUST carry status "error" (got ${JSON.stringify(first['status'])})`));
65
+ }
66
+
67
+ if (second === undefined) {
68
+ out.push(f(false, B, 'the second scripted call MUST have an agent.tool-returned with the same callId'));
69
+ } else {
70
+ out.push(f('outcome' in second && second['outcome'] !== undefined, B, 'the call served by the following success entry MUST carry outcome'));
71
+ out.push(f(second['error'] === undefined, B, 'the call served by the following success entry MUST NOT carry error'));
72
+ }
73
+
74
+ const tasks = Array.isArray(summary['tasks']) ? (summary['tasks'] as unknown[]).filter(isRec) : [];
75
+ const task = tasks.find((t) => t['taskId'] === FIXTURE_ERROR_TASK);
76
+ out.push(f(task !== undefined, B, `the summary MUST carry task ${FIXTURE_ERROR_TASK}`));
77
+ if (task !== undefined) out.push(f((task['fixtureMissCount'] ?? 0) === 0, B, `a served error entry is not a miss: fixtureMissCount MUST be 0 (got ${JSON.stringify(task['fixtureMissCount'])})`));
78
+ return out;
79
+ }
@@ -44,8 +44,21 @@
44
44
  * clears `passScore` 0.6, the gating aggregate 0.5 does not, so the suite
45
45
  * does not pass. An RFC 0244-only host reports `passed: true` on both.
46
46
  *
47
+ * RFC 0245: a fixture entry marked `isError: true` is replayed as a tool
48
+ * error, never as a success. Judge in `lib/eval-fixture-error-witness.ts`.
49
+ * 10. Server-free — an `isError: true` entry validates; a string `"true"` is
50
+ * refused; `conformance-eval-fixture-error` validates (leg 2).
51
+ * 11. `conformance-eval-fixture-error` (fixtureErrors + fixtureMatching) —
52
+ * the call served by the error entry is recorded with `error` and no
53
+ * `outcome` (`status: "error"` under tool hooks), the next with
54
+ * `outcome`, and `fixtureMissCount` is 0. The two-call sequence needs
55
+ * RFC 0244 §D's first-unconsumed selection, hence `fixtureMatching`.
56
+ * 12. Refusal — a host advertising `agents.evalSuite` without `fixtureErrors`
57
+ * that lists the fixture refuses it with `400 validation_error`.
58
+ *
47
59
  * @see RFCS/0244-eval-trajectories-trials-and-recorded-fixtures.md
48
60
  * @see RFCS/0246-trajectory-failure-gates-the-pass.md
61
+ * @see RFCS/0245-eval-fixtures-replay-tool-errors.md
49
62
  */
50
63
 
51
64
  import { describe, it, expect } from 'vitest';
@@ -62,6 +75,7 @@ import { FIXTURES_DIR } from '../lib/paths.js';
62
75
  import { pollTerminal, readEvents, isRecord, type Ev } from '../lib/fixture-run-observer.js';
63
76
  import { gradeTrajectory, passAtK, passHatK, scoreStderr, withinTolerance, type TrajectorySpec } from '../lib/eval-trajectory.js';
64
77
  import { judgeMeanGated, judgeSuiteGated } from '../lib/eval-gate-witness.js';
78
+ import { judgeFixtureErrorReplayed } from '../lib/eval-fixture-error-witness.js';
65
79
 
66
80
  const PROFILE = majorProfile(2);
67
81
  const UNREADABLE = 'v2 discovery unreachable — /.well-known/openwop did not answer 200 JSON under OpenWOP-Version: 2.0';
@@ -80,8 +94,12 @@ const ID_GATED_SHAPE = 'openwop.requirement.0246.gated-fields-shape';
80
94
  const ID_MEAN_GATED = 'openwop.requirement.0246.mean-gated';
81
95
  const ID_SUITE_GATED = 'openwop.requirement.0246.suite-gated';
82
96
  const DOC_0246 = 'eval-summary.schema.json gatedScore / gatedAggregateScore (RFC 0246)';
97
+ const ID_FIXTURE_ERROR_SHAPE = 'openwop.requirement.0245.fixture-error-shape';
98
+ const ID_FIXTURE_ERROR = 'openwop.requirement.0245.fixture-error-replayed';
99
+ const ID_FIXTURE_ERROR_REFUSED = 'openwop.requirement.0245.unadvertised-refused';
100
+ const DOC_0245 = 'agents.schema.json §evalSuite.fixtureErrors (RFC 0245)';
83
101
 
84
- interface Task { taskId: string; input: { script: Array<{ tool: string; arguments?: unknown }>; output: string }; expected: { kind: string; trajectory?: TrajectorySpec } }
102
+ interface Task { taskId: string; input: { script: Array<{ tool: string; arguments?: unknown }>; output: string }; expected: { kind: string; trajectory?: TrajectorySpec }; fixtures?: { toolResponses?: Array<Record<string, unknown>> } }
85
103
  interface Suite { tasks: Task[]; trials?: { count: number; k?: number; passRule?: string } }
86
104
  const fixture = (id: string): Suite => JSON.parse(readFileSync(join(FIXTURES_DIR, 'eval-suites', `${id}.json`), 'utf8')) as Suite;
87
105
 
@@ -160,7 +178,7 @@ describe('v2 RFC 0244 — wire shape (server-free)', () => {
160
178
  });
161
179
 
162
180
  it('the eval-suite fixtures validate against the v2 suite schema', () => {
163
- for (const id of ['conformance-eval-trajectory', 'conformance-eval-trials', 'conformance-eval-fixture-miss', 'conformance-eval-trajectory-gate', 'conformance-eval-trajectory-gate-mean']) {
181
+ for (const id of ['conformance-eval-trajectory', 'conformance-eval-trials', 'conformance-eval-fixture-miss', 'conformance-eval-trajectory-gate', 'conformance-eval-trajectory-gate-mean', 'conformance-eval-fixture-error']) {
164
182
  const r = suiteSchema(fixture(id));
165
183
  expect(r.ok, req(ID_FIXTURES, 'fixtures.md §eval-suites', `${id} MUST validate: ${r.errors}`)).toBe(true);
166
184
  }
@@ -173,6 +191,14 @@ describe('v2 RFC 0244 — wire shape (server-free)', () => {
173
191
  expect(summarySchema({ ...ok, gatedAggregateScore: 1.5 }).ok, req(ID_GATED_SHAPE, DOC_0246, 'gatedAggregateScore above 1 MUST be refused')).toBe(false);
174
192
  expect(summarySchema({ ...ok, tasks: [{ taskId: 't', score: 1, gatedScore: -0.1, passed: false }] }).ok, req(ID_GATED_SHAPE, DOC_0246, 'a negative gatedScore MUST be refused')).toBe(false);
175
193
  });
194
+
195
+ it('a fixture entry may be marked isError, and only as a boolean (RFC 0245 §A)', () => {
196
+ const base = fixture('conformance-eval-fixture-error');
197
+ const withIsError = (v: unknown): boolean => { const s = structuredClone(base) as Suite; s.tasks[0]!.fixtures!.toolResponses![0]!['isError'] = v; return suiteSchema(s).ok; };
198
+ expect(withIsError(true), req(ID_FIXTURE_ERROR_SHAPE, DOC_0245, `an isError: true entry MUST validate: ${suiteSchema(base).errors}`)).toBe(true);
199
+ expect(withIsError(false), req(ID_FIXTURE_ERROR_SHAPE, DOC_0245, 'an isError: false entry MUST validate')).toBe(true);
200
+ expect(withIsError('true'), req(ID_FIXTURE_ERROR_SHAPE, DOC_0245, 'a string isError MUST be refused')).toBe(false);
201
+ });
176
202
  });
177
203
 
178
204
  describe('v2 RFC 0244 — host behaviour', () => {
@@ -305,4 +331,34 @@ describe('v2 RFC 0244 — host behaviour', () => {
305
331
  if (o.kind === 'skip') return softSkip(o.disposition, o.reason);
306
332
  for (const x of judgeSuiteGated(o.summary, 0.6)) expect(x.ok, req(ID_SUITE_GATED, x.doc, x.message)).toBe(true);
307
333
  });
334
+
335
+ it('a fixture entry marked isError is replayed as a tool error, not a success (RFC 0245)', async () => {
336
+ const { doc, facet } = await evalSuiteFacet();
337
+ if (!doc) return softSkip('blocked', UNREADABLE);
338
+ if (facet?.['fixtureErrors'] !== true) return softSkip('inapplicable', 'the host does not advertise agents.evalSuite.fixtureErrors');
339
+ if (facet['fixtureMatching'] !== true) return softSkip('inapplicable', 'the host does not advertise agents.evalSuite.fixtureMatching, which the error-then-success sequence needs (RFC 0244 §D first unconsumed match)');
340
+ const id = 'conformance-eval-fixture-error';
341
+ if (!isFixtureAdvertised(id)) return softSkip('inapplicable', `the host does not advertise fixture ${id}`);
342
+ const o = await runSuite(id);
343
+ if (o.kind === 'skip') return softSkip(o.disposition, o.reason);
344
+ const hooks = doc['toolHooks'];
345
+ const prePostEvents = isRecord(hooks) && hooks['prePostEvents'] === true;
346
+ for (const x of judgeFixtureErrorReplayed(o.events, o.summary, { agentId: AGENT, prePostEvents })) expect(x.ok, req(ID_FIXTURE_ERROR, x.doc, x.message)).toBe(true);
347
+ });
348
+
349
+ it('a suite using isError is refused at createRun without fixtureErrors (RFC 0245 §C)', async () => {
350
+ const { doc, facet } = await evalSuiteFacet();
351
+ if (!doc) return softSkip('blocked', UNREADABLE);
352
+ if (facet === null) return softSkip('inapplicable', 'the host does not advertise agents.evalSuite (v2-eval-mode-unadvertised-refused covers it)');
353
+ if (facet['fixtureErrors'] === true) return softSkip('inapplicable', 'the host advertises agents.evalSuite.fixtureErrors, so the isError suite is served');
354
+ const id = 'conformance-eval-fixture-error';
355
+ if (!isFixtureAdvertised(id)) return softSkip('inapplicable', `the host does not advertise fixture ${id}`);
356
+ const res = await http(() => driver.post(PROFILE.runsPath, { mode: 'eval', evalSuiteRef: SUITE_URI(id), agentId: AGENT }));
357
+ if (res === null) return softSkip('blocked', 'POST /runs unreachable (fetch failed)');
358
+ if (res.status === 429) return softSkip('blocked', 'POST /runs answered 429 — the run budget, not the wire');
359
+ const runId = isRecord(res.json) && typeof res.json['runId'] === 'string' ? res.json['runId'] : undefined;
360
+ if (res.status === 201 && runId !== undefined) await http(() => driver.post(`${PROFILE.runsPath}/${PROFILE.idSegment(runId)}/cancel`, {}));
361
+ expect(res.status, req(ID_FIXTURE_ERROR_REFUSED, DOC_0245, `an isError suite on a host without agents.evalSuite.fixtureErrors MUST be refused with 400 (got ${res.status})`)).toBe(400);
362
+ expect(readErrorCode(res.json), req(ID_FIXTURE_ERROR_REFUSED, DOC_0245, 'the refusal MUST carry validation_error')).toBe('validation_error');
363
+ });
308
364
  });