@openwop/openwop-conformance 2.46.43 → 2.46.44
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 +5 -0
- package/README.md +2 -2
- package/dist/spec-artifacts.lock.json +2 -2
- package/fixtures/conformance-loopback-refine-fail.json +92 -0
- package/fixtures/conformance-loopback-refine.json +91 -0
- package/fixtures.md +25 -0
- package/package.json +2 -2
- package/requirements.json +314 -20
- package/scenario-majors.json +8 -2
- package/schemas/CORPUS-STAMP.json +22 -21
- package/src/lib/bounded-loopback-witness.ts +125 -0
- package/src/scenarios/fixtures-valid.test.ts +14 -2
- package/src/scenarios/v2-bounded-loopback.test.ts +231 -0
- package/src/scenarios/v2-interrupt-resolved-via.test.ts +257 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## [2.46.44] — 2026-11-08 — Bounded loopback and resolvedVia go Active
|
|
2
|
+
|
|
3
|
+
- **RFC 0258.** `v2-bounded-loopback`: a server-free shape leg and six host legs (re-arm, history from the log, `ApprovalData.loopback`, withdraw, fail on exhaustion, replay re-arm), gated on the `loopback` family and the `conformance-loopback-refine` / `-fail` fixtures. Judges in `lib/bounded-loopback-witness.ts`.
|
|
4
|
+
- **RFC 0259.** `v2-interrupt-resolved-via`: a server-free shape leg and six host legs on the `readInterruptToken` seam, gated on `interrupt.resolvedVia`.
|
|
5
|
+
|
|
1
6
|
## [2.46.43] — 2026-11-04 — Two RFCs filed as Draft: bounded loopback and resolvedVia
|
|
2
7
|
|
|
3
8
|
- **No suite change.** RFCs 0258 and 0259 add gap rows, which change the packed `spec/v1/gaps.json` and `CORPUS-STAMP.json`; no scenario, fixture or schema moves.
|
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
|
|
140
|
+
The current suite has 668 scenario files under `src/scenarios/`. By target major ([`scenario-majors.json`](./scenario-majors.json)): 228 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:
|
|
169
|
+
Current source tree: 668 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
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conformance-loopback-refine-fail",
|
|
3
|
+
"name": "Conformance: Bounded loopback (refine, fail on exhaustion)",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"description": "RFC 0258 §D. The same graph with maxIterations 1 and onExhausted: fail. The first refine loops; a refine on the exhausted second visit emits workflow.loopback-limit, then cap.breached {kind: loopback-iterations}, and fails the run with loop_limit_exceeded naming gate. Conformance scenario: v2-bounded-loopback.test.ts.",
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "draft",
|
|
9
|
+
"typeId": "core.identity",
|
|
10
|
+
"name": "Draft",
|
|
11
|
+
"position": {
|
|
12
|
+
"x": 0,
|
|
13
|
+
"y": 0
|
|
14
|
+
},
|
|
15
|
+
"config": {},
|
|
16
|
+
"inputs": {}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "gate",
|
|
20
|
+
"typeId": "core.approvalGate",
|
|
21
|
+
"name": "Review",
|
|
22
|
+
"position": {
|
|
23
|
+
"x": 200,
|
|
24
|
+
"y": 0
|
|
25
|
+
},
|
|
26
|
+
"config": {
|
|
27
|
+
"title": "Conformance loopback review (conformance-loopback-refine-fail)",
|
|
28
|
+
"description": "Conformance suite — refine to loop back to draft.",
|
|
29
|
+
"actions": [
|
|
30
|
+
"accept",
|
|
31
|
+
"reject",
|
|
32
|
+
"refine"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"inputs": {}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "apply",
|
|
39
|
+
"typeId": "core.noop",
|
|
40
|
+
"name": "Apply",
|
|
41
|
+
"position": {
|
|
42
|
+
"x": 400,
|
|
43
|
+
"y": 0
|
|
44
|
+
},
|
|
45
|
+
"config": {},
|
|
46
|
+
"inputs": {}
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"edges": [
|
|
50
|
+
{
|
|
51
|
+
"id": "draft-gate",
|
|
52
|
+
"sourceNodeId": "draft",
|
|
53
|
+
"targetNodeId": "gate"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "again",
|
|
57
|
+
"sourceNodeId": "gate",
|
|
58
|
+
"targetNodeId": "draft",
|
|
59
|
+
"loopback": {
|
|
60
|
+
"on": "refine",
|
|
61
|
+
"maxIterations": 1,
|
|
62
|
+
"historyInput": "rounds",
|
|
63
|
+
"onExhausted": "fail"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "gate-apply",
|
|
68
|
+
"sourceNodeId": "gate",
|
|
69
|
+
"targetNodeId": "apply",
|
|
70
|
+
"triggerRule": "all_success"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"triggers": [
|
|
74
|
+
{
|
|
75
|
+
"id": "manual",
|
|
76
|
+
"type": "manual",
|
|
77
|
+
"enabled": true
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"variables": [],
|
|
81
|
+
"metadata": {
|
|
82
|
+
"tags": [
|
|
83
|
+
"conformance",
|
|
84
|
+
"hitl",
|
|
85
|
+
"loopback",
|
|
86
|
+
"v2-only"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"settings": {
|
|
90
|
+
"timeout": 0
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conformance-loopback-refine",
|
|
3
|
+
"name": "Conformance: Bounded loopback (refine, withdraw)",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"description": "RFC 0258. draft (core.identity, echoes its inputs) → gate (core.approvalGate, accept/reject/refine) → apply (core.noop), plus a loopback edge gate → draft {on: refine, maxIterations: 2, historyInput: rounds}. Each refine re-runs draft and asks gate again; draft echoes the round history it received. After two refines the third visit omits refine (onExhausted defaults to withdraw), and accept runs apply once. Conformance scenario: v2-bounded-loopback.test.ts.",
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "draft",
|
|
9
|
+
"typeId": "core.identity",
|
|
10
|
+
"name": "Draft",
|
|
11
|
+
"position": {
|
|
12
|
+
"x": 0,
|
|
13
|
+
"y": 0
|
|
14
|
+
},
|
|
15
|
+
"config": {},
|
|
16
|
+
"inputs": {}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "gate",
|
|
20
|
+
"typeId": "core.approvalGate",
|
|
21
|
+
"name": "Review",
|
|
22
|
+
"position": {
|
|
23
|
+
"x": 200,
|
|
24
|
+
"y": 0
|
|
25
|
+
},
|
|
26
|
+
"config": {
|
|
27
|
+
"title": "Conformance loopback review (conformance-loopback-refine)",
|
|
28
|
+
"description": "Conformance suite — refine to loop back to draft.",
|
|
29
|
+
"actions": [
|
|
30
|
+
"accept",
|
|
31
|
+
"reject",
|
|
32
|
+
"refine"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"inputs": {}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "apply",
|
|
39
|
+
"typeId": "core.noop",
|
|
40
|
+
"name": "Apply",
|
|
41
|
+
"position": {
|
|
42
|
+
"x": 400,
|
|
43
|
+
"y": 0
|
|
44
|
+
},
|
|
45
|
+
"config": {},
|
|
46
|
+
"inputs": {}
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"edges": [
|
|
50
|
+
{
|
|
51
|
+
"id": "draft-gate",
|
|
52
|
+
"sourceNodeId": "draft",
|
|
53
|
+
"targetNodeId": "gate"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "again",
|
|
57
|
+
"sourceNodeId": "gate",
|
|
58
|
+
"targetNodeId": "draft",
|
|
59
|
+
"loopback": {
|
|
60
|
+
"on": "refine",
|
|
61
|
+
"maxIterations": 2,
|
|
62
|
+
"historyInput": "rounds"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "gate-apply",
|
|
67
|
+
"sourceNodeId": "gate",
|
|
68
|
+
"targetNodeId": "apply",
|
|
69
|
+
"triggerRule": "all_success"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"triggers": [
|
|
73
|
+
{
|
|
74
|
+
"id": "manual",
|
|
75
|
+
"type": "manual",
|
|
76
|
+
"enabled": true
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"variables": [],
|
|
80
|
+
"metadata": {
|
|
81
|
+
"tags": [
|
|
82
|
+
"conformance",
|
|
83
|
+
"hitl",
|
|
84
|
+
"loopback",
|
|
85
|
+
"v2-only"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"settings": {
|
|
89
|
+
"timeout": 0
|
|
90
|
+
}
|
|
91
|
+
}
|
package/fixtures.md
CHANGED
|
@@ -55,6 +55,8 @@ All fixtures MUST advertise:
|
|
|
55
55
|
| Error route only | `conformance-error-route-only` | RFC 0257 — the only terminal sits behind `any_failed`; it is skipped with no condition anywhere | `completed` with `skippedTerminals` | < 1s |
|
|
56
56
|
| All terminals skipped | `conformance-all-terminals-skipped` | RFC 0257 — a `truthy` edge on `payload.ok` declines (`{ok:false}`) or fires (`{ok:true}`) | `completed`, with or without `skippedTerminals` | < 1s |
|
|
57
57
|
| Sub-workflow (child reaches no terminal) | `conformance-subworkflow-skip-parent` | RFC 0257 §B — a `core.subWorkflow` child completes with `skippedTerminals`; the parent continues | `completed` | < 2s |
|
|
58
|
+
| Bounded loopback (withdraw) | `conformance-loopback-refine` | RFC 0258 — a refine loops `gate` back to `draft` with the round history; after two refines `refine` is withdrawn | `completed` after accept | unbounded (suspends) |
|
|
59
|
+
| Bounded loopback (fail) | `conformance-loopback-refine-fail` | RFC 0258 §D — `maxIterations: 1`, `onExhausted: fail`; a refine on the exhausted visit fails the run | `failed` (`loop_limit_exceeded`) | unbounded (suspends) |
|
|
58
60
|
| Approval (timeout) | `conformance-approval-timeout` | RFC 0223 G2 — a 1500 ms `timeoutMs`, no `onTimeout`: the host's timer resolves the gate rejected | `failed` (`approval_rejected`) after the timeout | ~1.5 s + the host's sweep |
|
|
59
61
|
| Approval (timeout, onTimeout approve) | `conformance-approval-timeout-approve` | openwop#1696 — a 1500 ms `timeoutMs` with `onTimeout: approve`: a timeout never grants, so the gate still resolves rejected | `failed` (`approval_rejected`) after the timeout | ~1.5 s + the host's sweep |
|
|
60
62
|
| Approval (reject loops back) | `conformance-approval-reject-loopback` | openwop#1697 — a rejected gate routed back to itself through `revise` is asked again under a new `key` | `waiting-approval` again after one reject (the suite cancels) | unbounded (suspends) |
|
|
@@ -308,6 +310,29 @@ The `messages`-mode stream fixture (AI token streaming) is covered by the determ
|
|
|
308
310
|
4. With `{ok: true}`, `act` MUST run and the run MUST end `run.completed` with no `skippedTerminals`.
|
|
309
311
|
- **Terminal status**: `completed` on both inputs.
|
|
310
312
|
|
|
313
|
+
### `conformance-loopback-refine`
|
|
314
|
+
|
|
315
|
+
- **Tagged** `v2-only`: `fixtures-valid` validates it against `schemas/v2/workflow-definition.schema.json`, because the frozen v1 wire cannot carry `loopback`.
|
|
316
|
+
- **Purpose**: witness RFC 0258 §§C–D: the re-arm, the round history and withdrawal on exhaustion.
|
|
317
|
+
- **Inputs**: none.
|
|
318
|
+
- **Behavior**:
|
|
319
|
+
1. `draft` (`core.identity`) echoes its inputs. Its only input is the loopback's `historyInput` port `rounds`, so its `node.completed` `outputs` is `{rounds: [...]}`: `[]` on the first visit, then one `{round, outputs, refineFeedback}` entry per round, oldest first.
|
|
320
|
+
2. `gate` (`core.approvalGate`, actions accept/reject/refine) suspends. Each request's `ApprovalData.loopback` reads `{round, maxIterations: 2, exhausted}`.
|
|
321
|
+
3. A `refine` with `refineFeedback` completes `gate` with the marker `loopback {edgeId: "again", targetNodeId: "draft", round, rearmed: ["draft", "gate"]}`; `draft` runs again and `gate` is asked again under a new key. `apply` MUST NOT start.
|
|
322
|
+
4. After two refines, the third request omits `refine`, and a `refine` resume answers `400 validation_error` with the run still `waiting-approval`.
|
|
323
|
+
5. `accept` completes `gate`; `apply` runs once.
|
|
324
|
+
- **Terminal status**: `completed`.
|
|
325
|
+
- **Requires**: the `loopback` family with `maxIterations >= 2`.
|
|
326
|
+
|
|
327
|
+
### `conformance-loopback-refine-fail`
|
|
328
|
+
|
|
329
|
+
- **Tagged** `v2-only`: `fixtures-valid` validates it against `schemas/v2/workflow-definition.schema.json`, because the frozen v1 wire cannot carry `loopback`.
|
|
330
|
+
- **Purpose**: witness RFC 0258 §D `onExhausted: fail`.
|
|
331
|
+
- **Inputs**: none.
|
|
332
|
+
- **Behavior**: the same graph with `maxIterations: 1` and `onExhausted: "fail"`. The first refine loops. A refine on the exhausted second visit emits `workflow.loopback-limit {nodeId: "gate", iterations: 1, limit: 1}`, then `cap.breached {kind: "loopback-iterations", limit: 1, observed: 2}`, then `run.failed` with `loop_limit_exceeded` and `failedNodeId: "gate"`.
|
|
333
|
+
- **Terminal status**: `failed`.
|
|
334
|
+
- **Requires**: the `loopback` family.
|
|
335
|
+
|
|
311
336
|
### `conformance-subworkflow-skip-parent`
|
|
312
337
|
|
|
313
338
|
- **Purpose**: witness RFC 0257 §B (`spec/v2/core/execution.md` §`subWorkflow`, Output).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwop/openwop-conformance",
|
|
3
|
-
"version": "2.46.
|
|
3
|
+
"version": "2.46.44",
|
|
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.
|
|
59
|
+
"@openwop/spec-artifacts": "2.46.44"
|
|
60
60
|
}
|
|
61
61
|
}
|