@openwop/openwop-conformance 2.45.0 → 2.45.2

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/spec-artifacts.lock.json +2 -2
  3. package/package.json +2 -2
  4. package/requirements.json +138 -133
  5. package/schemas/CORPUS-STAMP.json +10 -10
  6. package/src/lib/run-list-walk.ts +65 -0
  7. package/src/scenarios/agentConfidenceEscalation.test.ts +8 -1
  8. package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +8 -1
  9. package/src/scenarios/agentMemoryRedactionContract.test.ts +8 -1
  10. package/src/scenarios/agentMemoryRoundTrip.test.ts +8 -1
  11. package/src/scenarios/agentMemoryTtlExpiry.test.ts +8 -1
  12. package/src/scenarios/agentMessageReducer.test.ts +8 -1
  13. package/src/scenarios/agentMetadata.test.ts +8 -1
  14. package/src/scenarios/agentPackCatalog.test.ts +13 -5
  15. package/src/scenarios/agentPackExport.test.ts +8 -2
  16. package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +17 -2
  17. package/src/scenarios/agentPackInstall.test.ts +8 -2
  18. package/src/scenarios/agentPackProvenance.test.ts +8 -1
  19. package/src/scenarios/agentReasoningEvents.test.ts +9 -1
  20. package/src/scenarios/agentReasoningStreaming.test.ts +14 -1
  21. package/src/scenarios/append-ordering.test.ts +7 -2
  22. package/src/scenarios/bulk-cancel.test.ts +11 -1
  23. package/src/scenarios/cap-breach.test.ts +8 -1
  24. package/src/scenarios/channel-ttl.test.ts +7 -1
  25. package/src/scenarios/conversationCapabilityNegotiation.test.ts +8 -1
  26. package/src/scenarios/conversationLifecycle.test.ts +8 -1
  27. package/src/scenarios/conversationReplayDeterminism.test.ts +8 -2
  28. package/src/scenarios/conversationVsLegacySuspend.test.ts +8 -1
  29. package/src/scenarios/cost-attribution.test.ts +17 -4
  30. package/src/scenarios/debugBundle.test.ts +16 -5
  31. package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -2
  32. package/src/scenarios/dispatch-output-mapping.test.ts +20 -4
  33. package/src/scenarios/dispatchLoop.test.ts +7 -1
  34. package/src/scenarios/durability-poison-exhaustion.test.ts +7 -2
  35. package/src/scenarios/highConcurrency.test.ts +11 -1
  36. package/src/scenarios/host-callback-declaration.test.ts +10 -1
  37. package/src/scenarios/identity-passthrough.test.ts +7 -1
  38. package/src/scenarios/interrupt-approval.test.ts +25 -5
  39. package/src/scenarios/interrupt-auth-required-resume.test.ts +9 -3
  40. package/src/scenarios/interrupt-external-event-correlation.test.ts +9 -2
  41. package/src/scenarios/interrupt-parent-child-cascade.test.ts +10 -2
  42. package/src/scenarios/interrupt-quorum-resolution.test.ts +9 -2
  43. package/src/scenarios/interrupt-token-matrix.test.ts +11 -3
  44. package/src/scenarios/interruptRace.test.ts +13 -3
  45. package/src/scenarios/multi-agent-confidence-escalation.test.ts +14 -3
  46. package/src/scenarios/multi-agent-handoff-state-machine.test.ts +15 -3
  47. package/src/scenarios/multi-node-ordering.test.ts +7 -1
  48. package/src/scenarios/node-failed-payload-shape.test.ts +7 -1
  49. package/src/scenarios/orchestratorConservativePath.test.ts +8 -1
  50. package/src/scenarios/orchestratorDispatch.test.ts +9 -1
  51. package/src/scenarios/orchestratorTermination.test.ts +8 -1
  52. package/src/scenarios/owner-subject-echo.test.ts +9 -2
  53. package/src/scenarios/pause-resume.test.ts +19 -8
  54. package/src/scenarios/prompt-all-four-kinds-events.test.ts +9 -2
  55. package/src/scenarios/prompt-end-to-end-events.test.ts +10 -2
  56. package/src/scenarios/replay-fork-arbitrary.test.ts +11 -4
  57. package/src/scenarios/restart-during-run.test.ts +7 -1
  58. package/src/scenarios/secret-leakage-otel-attribute.test.ts +14 -5
  59. package/src/scenarios/stream-text-fixture.test.ts +8 -2
  60. package/src/scenarios/streamReconnect.test.ts +8 -2
  61. package/src/scenarios/subworkflow.test.ts +9 -1
  62. package/src/scenarios/v2-error-registry.test.ts +7 -10
  63. package/src/scenarios/v2-run-list.test.ts +14 -16
  64. package/src/scenarios/version-fold.test.ts +8 -2
@@ -12,6 +12,7 @@ import { driver } from '../lib/driver.js';
12
12
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
13
13
  import { isFixtureAdvertised } from '../lib/fixtures.js';
14
14
  import { req } from '../lib/requirement-ids.js';
15
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
15
16
 
16
17
  const WORKFLOW_ID = 'conformance-approval';
17
18
  const NODE_ID = 'gate';
@@ -23,8 +24,21 @@ const SKIP_NO_REFINE = !isFixtureAdvertised(REFINE_WORKFLOW_ID);
23
24
  const EDIT_ACCEPT_WORKFLOW_ID = 'conformance-approval-edit-accept';
24
25
  const SKIP_NO_EDIT_ACCEPT = !isFixtureAdvertised(EDIT_ACCEPT_WORKFLOW_ID);
25
26
 
26
- describe.skipIf(SKIP_NO_FIXTURE)('interrupt: approval accept resumes to `completed`', () => {
27
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
28
+ const GATE_WHY: readonly [SoftSkipKind, string] =
29
+ (!isFixtureAdvertised(WORKFLOW_ID)) ? ['blocked', `the \`${WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
30
+
31
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
32
+ const GATE_WHY_2: readonly [SoftSkipKind, string] =
33
+ (!isFixtureAdvertised(REFINE_WORKFLOW_ID)) ? ['blocked', `the \`${REFINE_WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
34
+
35
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
36
+ const GATE_WHY_3: readonly [SoftSkipKind, string] =
37
+ (!isFixtureAdvertised(EDIT_ACCEPT_WORKFLOW_ID)) ? ['blocked', `the \`${EDIT_ACCEPT_WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
38
+
39
+ describe('interrupt: approval accept resumes to `completed`', () => {
27
40
  it('run suspends at gate, accept resolution drives terminal completed', async () => {
41
+ if (SKIP_NO_FIXTURE) return softSkip(...GATE_WHY);
28
42
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
29
43
  expect(create.status).toBe(201);
30
44
  const runId = (create.json as { runId: string }).runId;
@@ -52,8 +66,9 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: approval accept resumes to `complet
52
66
  });
53
67
  });
54
68
 
55
- describe.skipIf(SKIP_NO_FIXTURE)('interrupt: invalid resolve payload rejected per resumeSchema', () => {
69
+ describe('interrupt: invalid resolve payload rejected per resumeSchema', () => {
56
70
  it('400 (or 422) when action is not in {accept, reject}', async () => {
71
+ if (SKIP_NO_FIXTURE) return softSkip(...GATE_WHY);
57
72
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
58
73
  expect(create.status).toBe(201);
59
74
  const runId = (create.json as { runId: string }).runId;
@@ -80,8 +95,9 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: invalid resolve payload rejected pe
80
95
  });
81
96
  });
82
97
 
83
- describe.skipIf(SKIP_NO_FIXTURE)('interrupt: resolving an unknown interrupt returns 404', () => {
98
+ describe('interrupt: resolving an unknown interrupt returns 404', () => {
84
99
  it('400/404 when nodeId does not match an active interrupt', async () => {
100
+ if (SKIP_NO_FIXTURE) return softSkip(...GATE_WHY);
85
101
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
86
102
  expect(create.status).toBe(201);
87
103
  const runId = (create.json as { runId: string }).runId;
@@ -116,8 +132,9 @@ describe.skipIf(SKIP_NO_FIXTURE)('interrupt: resolving an unknown interrupt retu
116
132
  * adding `refine` to that fixture's `actions` would change a registered workflow
117
133
  * definition every host already serves.
118
134
  */
119
- describe.skipIf(SKIP_NO_REFINE)('interrupt: refine resolution carries action + refineFeedback (RFC 0183)', () => {
135
+ describe('interrupt: refine resolution carries action + refineFeedback (RFC 0183)', () => {
120
136
  it('a refine resolve round-trips `action` and the structured feedback it requires', async () => {
137
+ if (SKIP_NO_REFINE) return softSkip(...GATE_WHY_2);
121
138
  const create = await driver.post('/v1/runs', { workflowId: REFINE_WORKFLOW_ID });
122
139
  expect(create.status).toBe(201);
123
140
  const runId = (create.json as { runId: string }).runId;
@@ -168,6 +185,7 @@ describe.skipIf(SKIP_NO_REFINE)('interrupt: refine resolution carries action + r
168
185
  });
169
186
 
170
187
  it('refuses a refine resolution that supplies no refineFeedback', async () => {
188
+ if (SKIP_NO_REFINE) return softSkip(...GATE_WHY_2);
171
189
  const create = await driver.post('/v1/runs', { workflowId: REFINE_WORKFLOW_ID });
172
190
  expect(create.status).toBe(201);
173
191
  const runId = (create.json as { runId: string }).runId;
@@ -193,8 +211,9 @@ describe.skipIf(SKIP_NO_REFINE)('interrupt: refine resolution carries action + r
193
211
  * witnesses one of §A.2's two conditional MUSTs; until this block the other was
194
212
  * stated in `spec/v2/core/interrupt.md` §Approval and witnessed nowhere.
195
213
  */
196
- describe.skipIf(SKIP_NO_EDIT_ACCEPT)('interrupt: edit-accept resolution carries action + editedArtifactData (RFC 0183)', () => {
214
+ describe('interrupt: edit-accept resolution carries action + editedArtifactData (RFC 0183)', () => {
197
215
  it('an edit-accept resolve round-trips `action` and the edited artifact it requires', async () => {
216
+ if (SKIP_NO_EDIT_ACCEPT) return softSkip(...GATE_WHY_3);
198
217
  const create = await driver.post('/v1/runs', { workflowId: EDIT_ACCEPT_WORKFLOW_ID });
199
218
  expect(create.status).toBe(201);
200
219
  const runId = (create.json as { runId: string }).runId;
@@ -245,6 +264,7 @@ describe.skipIf(SKIP_NO_EDIT_ACCEPT)('interrupt: edit-accept resolution carries
245
264
  });
246
265
 
247
266
  it('refuses an edit-accept resolution that supplies no editedArtifactData', async () => {
267
+ if (SKIP_NO_EDIT_ACCEPT) return softSkip(...GATE_WHY_3);
248
268
  const create = await driver.post('/v1/runs', { workflowId: EDIT_ACCEPT_WORKFLOW_ID });
249
269
  expect(create.status).toBe(201);
250
270
  const runId = (create.json as { runId: string }).runId;
@@ -24,14 +24,19 @@ import { driver } from '../lib/driver.js';
24
24
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
25
25
  import { isFixtureAdvertised } from '../lib/fixtures.js';
26
26
  import { req } from '../lib/requirement-ids.js';
27
- import { softSkip } from '../lib/soft-skip.js';
27
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
28
28
 
29
29
  const WORKFLOW_ID = 'conformance-interrupt-auth-required';
30
30
  const NODE_ID = 'gate';
31
31
  const SKIP = !isFixtureAdvertised(WORKFLOW_ID);
32
32
 
33
- describe.skipIf(SKIP)('interrupt: auth-required — bearer resume succeeds', () => {
33
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
34
+ const GATE_WHY: readonly [SoftSkipKind, string] =
35
+ (!isFixtureAdvertised(WORKFLOW_ID)) ? ['blocked', `the \`${WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
36
+
37
+ describe('interrupt: auth-required — bearer resume succeeds', () => {
34
38
  it('valid bearer with approvals:respond drives terminal completed', async () => {
39
+ if (SKIP) return softSkip(...GATE_WHY);
35
40
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
36
41
  expect(create.status).toBe(201);
37
42
  const runId = (create.json as { runId: string }).runId;
@@ -53,8 +58,9 @@ describe.skipIf(SKIP)('interrupt: auth-required — bearer resume succeeds', ()
53
58
  });
54
59
  });
55
60
 
56
- describe.skipIf(SKIP)('interrupt: auth-required — insufficient scope returns 403', () => {
61
+ describe('interrupt: auth-required — insufficient scope returns 403', () => {
57
62
  it('bearer without approvals:respond scope is rejected', async () => {
63
+ if (SKIP) return softSkip(...GATE_WHY);
58
64
  // This scenario requires a separate test-only credential that lacks
59
65
  // `approvals:respond`. Drivers wire it via the OPENWOP_TEST_LOW_SCOPE_KEY
60
66
  // env var; when absent the test skips (rather than passing trivially).
@@ -23,6 +23,7 @@ import { driver } from '../lib/driver.js';
23
23
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
24
24
  import { isFixtureAdvertised } from '../lib/fixtures.js';
25
25
  import { req } from '../lib/requirement-ids.js';
26
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
26
27
 
27
28
  const WORKFLOW_ID = 'conformance-interrupt-external-event';
28
29
  const SKIP = !isFixtureAdvertised(WORKFLOW_ID);
@@ -43,8 +44,13 @@ async function fetchInterruptToken(runId: string): Promise<string | null> {
43
44
  return null;
44
45
  }
45
46
 
46
- describe.skipIf(SKIP)('interrupt: external-event — matching correlation resumes', () => {
47
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
48
+ const GATE_WHY: readonly [SoftSkipKind, string] =
49
+ (!isFixtureAdvertised(WORKFLOW_ID)) ? ['blocked', `the \`${WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
50
+
51
+ describe('interrupt: external-event — matching correlation resumes', () => {
47
52
  it('signed-token POST with matching correlation drives terminal completed', async () => {
53
+ if (SKIP) return softSkip(...GATE_WHY);
48
54
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
49
55
  expect(create.status).toBe(201);
50
56
  const runId = (create.json as { runId: string }).runId;
@@ -78,8 +84,9 @@ describe.skipIf(SKIP)('interrupt: external-event — matching correlation resume
78
84
  });
79
85
  });
80
86
 
81
- describe.skipIf(SKIP)('interrupt: external-event — mismatched correlation rejected', () => {
87
+ describe('interrupt: external-event — mismatched correlation rejected', () => {
82
88
  it('correlation mismatch returns 422 and leaves run suspended', async () => {
89
+ if (SKIP) return softSkip(...GATE_WHY);
83
90
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
84
91
  expect(create.status).toBe(201);
85
92
  const runId = (create.json as { runId: string }).runId;
@@ -22,6 +22,7 @@ import { driver } from '../lib/driver.js';
22
22
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
23
23
  import { isFixtureAdvertised } from '../lib/fixtures.js';
24
24
  import { req } from '../lib/requirement-ids.js';
25
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
25
26
 
26
27
  const PARENT_WORKFLOW = 'conformance-interrupt-parent-child-cancel';
27
28
  const CHILD_WORKFLOW = 'conformance-interrupt-parent-child-cancel-child';
@@ -41,8 +42,14 @@ async function findChildRunId(parentRunId: string): Promise<string | null> {
41
42
  return child?.runId ?? null;
42
43
  }
43
44
 
44
- describe.skipIf(SKIP)('interrupt: parent/child — parent cancel cascades to child', () => {
45
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
46
+ const GATE_WHY: readonly [SoftSkipKind, string] =
47
+ (!isFixtureAdvertised(PARENT_WORKFLOW)) ? ['blocked', `the \`${PARENT_WORKFLOW}\` fixture is not advertised`] as const :
48
+ (!isFixtureAdvertised(CHILD_WORKFLOW)) ? ['blocked', `the \`${CHILD_WORKFLOW}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
49
+
50
+ describe('interrupt: parent/child — parent cancel cascades to child', () => {
45
51
  it('child transitions to cancelled when parent is cancelled mid-suspend', async () => {
52
+ if (SKIP) return softSkip(...GATE_WHY);
46
53
  const create = await driver.post('/v1/runs', { workflowId: PARENT_WORKFLOW });
47
54
  expect(create.status).toBe(201);
48
55
  const parentRunId = (create.json as { runId: string }).runId;
@@ -73,8 +80,9 @@ describe.skipIf(SKIP)('interrupt: parent/child — parent cancel cascades to chi
73
80
  });
74
81
  });
75
82
 
76
- describe.skipIf(SKIP)('interrupt: parent/child — post-cascade resolve of child returns 410/409', () => {
83
+ describe('interrupt: parent/child — post-cascade resolve of child returns 410/409', () => {
77
84
  it('attempting to resolve the cascaded child interrupt is rejected', async () => {
85
+ if (SKIP) return softSkip(...GATE_WHY);
78
86
  const create = await driver.post('/v1/runs', { workflowId: PARENT_WORKFLOW });
79
87
  expect(create.status).toBe(201);
80
88
  const parentRunId = (create.json as { runId: string }).runId;
@@ -22,6 +22,7 @@ import { driver } from '../lib/driver.js';
22
22
  import { pollUntilStatus, pollUntilTerminal } from '../lib/polling.js';
23
23
  import { isFixtureAdvertised } from '../lib/fixtures.js';
24
24
  import { req } from '../lib/requirement-ids.js';
25
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
25
26
 
26
27
  // Profile gating: a host claims `openwop-interrupt-quorum` support by
27
28
  // advertising the fixture. Hosts that don't support quorum semantics
@@ -30,8 +31,13 @@ const WORKFLOW_ID = 'conformance-interrupt-quorum';
30
31
  const NODE_ID = 'gate';
31
32
  const SKIP = !isFixtureAdvertised(WORKFLOW_ID);
32
33
 
33
- describe.skipIf(SKIP)('interrupt: quorum — three accepts resume to completed', () => {
34
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
35
+ const GATE_WHY: readonly [SoftSkipKind, string] =
36
+ (!isFixtureAdvertised(WORKFLOW_ID)) ? ['blocked', `the \`${WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
37
+
38
+ describe('interrupt: quorum — three accepts resume to completed', () => {
34
39
  it('first two accepts persist without resuming; third accept drives terminal completed', async () => {
40
+ if (SKIP) return softSkip(...GATE_WHY);
35
41
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
36
42
  expect(create.status).toBe(201);
37
43
  const runId = (create.json as { runId: string }).runId;
@@ -72,8 +78,9 @@ describe.skipIf(SKIP)('interrupt: quorum — three accepts resume to completed',
72
78
  });
73
79
  });
74
80
 
75
- describe.skipIf(SKIP)('interrupt: quorum — majority reject fails the gate', () => {
81
+ describe('interrupt: quorum — majority reject fails the gate', () => {
76
82
  it('two rejects out of three votes trigger the majority-reject termination', async () => {
83
+ if (SKIP) return softSkip(...GATE_WHY);
77
84
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
78
85
  expect(create.status).toBe(201);
79
86
  const runId = (create.json as { runId: string }).runId;
@@ -31,7 +31,7 @@ import { driver } from '../lib/driver.js';
31
31
  import { pollUntilStatus } from '../lib/polling.js';
32
32
  import { isFixtureAdvertised } from '../lib/fixtures.js';
33
33
  import { req } from '../lib/requirement-ids.js';
34
- import { blockedDespiteAssertions } from '../lib/soft-skip.js';
34
+ import { blockedDespiteAssertions, softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
35
35
 
36
36
  const FIXTURE = 'conformance-interrupt-external-event';
37
37
  const SKIP = !isFixtureAdvertised(FIXTURE);
@@ -42,8 +42,13 @@ function randomBytesB64(length: number): string {
42
42
  ).toString('base64url');
43
43
  }
44
44
 
45
- describe.skipIf(SKIP)('interrupt-token-matrix: GET /v1/interrupts/{token} negative paths', () => {
45
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
46
+ const GATE_WHY: readonly [SoftSkipKind, string] =
47
+ (!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
48
+
49
+ describe('interrupt-token-matrix: GET /v1/interrupts/{token} negative paths', () => {
46
50
  it('malformed token returns 400 or 404 (NEVER 200)', async () => {
51
+ if (SKIP) return softSkip(...GATE_WHY);
47
52
  const malformed = '!!!not-a-valid-token!!!';
48
53
  const res = await driver.get(`/v1/interrupts/${encodeURIComponent(malformed)}`);
49
54
  expect([400, 404]).toContain(res.status);
@@ -54,6 +59,7 @@ describe.skipIf(SKIP)('interrupt-token-matrix: GET /v1/interrupts/{token} negati
54
59
  });
55
60
 
56
61
  it('well-formed but unknown token returns 404', async () => {
62
+ if (SKIP) return softSkip(...GATE_WHY);
57
63
  // Plausibly-shaped opaque token that the host has no record of.
58
64
  const unknown = `tok_${randomBytesB64(32)}`;
59
65
  const res = await driver.get(`/v1/interrupts/${encodeURIComponent(unknown)}`);
@@ -64,8 +70,9 @@ describe.skipIf(SKIP)('interrupt-token-matrix: GET /v1/interrupts/{token} negati
64
70
  });
65
71
  });
66
72
 
67
- describe.skipIf(SKIP)('interrupt-token-matrix: POST /v1/interrupts/{token} negative paths', () => {
73
+ describe('interrupt-token-matrix: POST /v1/interrupts/{token} negative paths', () => {
68
74
  it('replay after successful resolve returns 409 or 404', async () => {
75
+ if (SKIP) return softSkip(...GATE_WHY);
69
76
  // Drive a run to suspension; capture the real token; resolve once;
70
77
  // replay the same POST and assert it doesn't succeed twice.
71
78
  const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
@@ -129,6 +136,7 @@ describe.skipIf(SKIP)('interrupt-token-matrix: POST /v1/interrupts/{token} negat
129
136
  });
130
137
 
131
138
  it('unknown token returns 404 on POST', async () => {
139
+ if (SKIP) return softSkip(...GATE_WHY);
132
140
  const unknown = `tok_${randomBytesB64(32)}`;
133
141
  const res = await driver.post(`/v1/interrupts/${encodeURIComponent(unknown)}`, {
134
142
  correlation: { orderId: 'noop', status: 'whatever' },
@@ -24,7 +24,7 @@ import { driver } from '../lib/driver.js';
24
24
  import { pollUntil } from '../lib/polling.js';
25
25
  import { isFixtureAdvertised } from '../lib/fixtures.js';
26
26
  import { req } from '../lib/requirement-ids.js';
27
- import { softSkip } from '../lib/soft-skip.js';
27
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
28
28
 
29
29
  const APPROVAL_WORKFLOW_ID = 'conformance-approval';
30
30
  const NOOP_WORKFLOW_ID = 'conformance-noop';
@@ -43,10 +43,19 @@ async function hostClaimsInterrupts(): Promise<boolean> {
43
43
  return (body.supportedEnvelopes as string[]).includes('clarification.request');
44
44
  }
45
45
 
46
- describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve dispatch deterministically', () => {
46
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
47
+ const GATE_WHY: readonly [SoftSkipKind, string] =
48
+ (!isFixtureAdvertised(APPROVAL_WORKFLOW_ID)) ? ['blocked', `the \`${APPROVAL_WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
49
+
50
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
51
+ const GATE_WHY_2: readonly [SoftSkipKind, string] =
52
+ (!isFixtureAdvertised(NOOP_WORKFLOW_ID)) ? ['blocked', `the \`${NOOP_WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
53
+
54
+ describe('interrupt-race: concurrent cancel + resolve dispatch deterministically', () => {
47
55
  it(
48
56
  'concurrent cancel + interrupt-resolve resolves to one of: cancelled or completed',
49
57
  async () => {
58
+ if (SKIP_NO_APPROVAL) return softSkip(...GATE_WHY);
50
59
  if (!(await hostClaimsInterrupts())) return softSkip('blocked', 'precondition not met — `!(await hostClaimsInterrupts())` returned early (skip-equivalent) (seam, prior step, or fixture unavailable)'); // skip-equivalent
51
60
 
52
61
  // Phase 1: start a workflow that suspends at an approval gate.
@@ -155,8 +164,9 @@ describe.skipIf(SKIP_NO_APPROVAL)('interrupt-race: concurrent cancel + resolve d
155
164
  );
156
165
  });
157
166
 
158
- describe.skipIf(SKIP_NO_NOOP)('interrupt-race: cancel against a non-suspended run is well-formed', () => {
167
+ describe('interrupt-race: cancel against a non-suspended run is well-formed', () => {
159
168
  it('cancel of a completed run returns 200 with the existing terminal status (idempotent)', async () => {
169
+ if (SKIP_NO_NOOP) return softSkip(...GATE_WHY_2);
160
170
  // Self-test that doesn't require a race. Runs against any host
161
171
  // that supports cancel (every conforming host does).
162
172
  const create = await driver.post('/v1/runs', { workflowId: 'conformance-noop' });
@@ -47,7 +47,7 @@
47
47
  */
48
48
 
49
49
  import { describe, it, expect } from 'vitest';
50
- import { softSkip } from '../lib/soft-skip.js';
50
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
51
51
  import { driver } from '../lib/driver.js';
52
52
  import { isFixtureAdvertised } from '../lib/fixtures.js';
53
53
  import { pollUntil } from '../lib/polling.js';
@@ -83,8 +83,13 @@ async function readDiscovery(): Promise<DiscoveryDoc | null> {
83
83
  }
84
84
  }
85
85
 
86
- describe.skipIf(HTTP_SKIP)('multi-agent-confidence-escalation: capability shape (RFC 0039 §A)', () => {
86
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
87
+ const GATE_WHY: readonly [SoftSkipKind, string] =
88
+ (!process.env.OPENWOP_BASE_URL) ? ['inapplicable', `no target: OPENWOP_BASE_URL is unset`] as const : ['blocked', 'the gate held for no named reason'] as const;
89
+
90
+ describe('multi-agent-confidence-escalation: capability shape (RFC 0039 §A)', () => {
87
91
  it('confidenceEscalationFloor (when advertised) MUST be in [0.5, 1.0]', async () => {
92
+ if (HTTP_SKIP) return softSkip(...GATE_WHY);
88
93
  const d = await readDiscovery();
89
94
  if (d === null) return softSkip('blocked', 'precondition not met — `d === null` returned early (seam, prior step, or fixture unavailable)');
90
95
  const em = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
@@ -101,8 +106,14 @@ describe.skipIf(HTTP_SKIP)('multi-agent-confidence-escalation: capability shape
101
106
  });
102
107
  });
103
108
 
104
- describe.skipIf(BEHAVIORAL_SKIP)('multi-agent-confidence-escalation: behavioral (RFC 0039 §A)', () => {
109
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
110
+ const GATE_WHY_2: readonly [SoftSkipKind, string] =
111
+ (!process.env.OPENWOP_BASE_URL) ? ['inapplicable', `no target: OPENWOP_BASE_URL is unset`] as const :
112
+ (!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
113
+
114
+ describe('multi-agent-confidence-escalation: behavioral (RFC 0039 §A)', () => {
105
115
  it('happy-path: low-confidence decision → confidence-escalated event + clarification interrupt + zero dispatch events', async () => {
116
+ if (BEHAVIORAL_SKIP) return softSkip(...GATE_WHY_2);
106
117
  const d = await readDiscovery();
107
118
  const supported = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel?.supported === true;
108
119
  const versionRaw = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel?.version;
@@ -31,7 +31,7 @@
31
31
  */
32
32
 
33
33
  import { describe, it, expect } from 'vitest';
34
- import { softSkip } from '../lib/soft-skip.js';
34
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
35
35
  import { driver } from '../lib/driver.js';
36
36
  import { executionModelVersionMax } from '../lib/execution-model-version.js';
37
37
  import { isFixtureAdvertised } from '../lib/fixtures.js';
@@ -62,8 +62,13 @@ async function readDiscovery(): Promise<DiscoveryDoc | null> {
62
62
  }
63
63
  }
64
64
 
65
- describe.skipIf(HTTP_SKIP)('multi-agent-handoff-state-machine: advertisement shape (RFC 0037 §C)', () => {
65
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
66
+ const GATE_WHY: readonly [SoftSkipKind, string] =
67
+ (!process.env.OPENWOP_BASE_URL) ? ['inapplicable', `no target: OPENWOP_BASE_URL is unset`] as const : ['blocked', 'the gate held for no named reason'] as const;
68
+
69
+ describe('multi-agent-handoff-state-machine: advertisement shape (RFC 0037 §C)', () => {
66
70
  it('capabilities.multiAgent.executionModel (when present) conforms to RFC 0037 §C', async () => {
71
+ if (HTTP_SKIP) return softSkip(...GATE_WHY);
67
72
  const d = await readDiscovery();
68
73
  if (d === null) return softSkip('blocked', 'discovery unavailable — skip (d === null)');
69
74
  const executionModel = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel;
@@ -108,8 +113,15 @@ const PARENT_FIXTURE = 'conformance-multi-agent-handoff';
108
113
  const CHILD_FIXTURE = 'conformance-multi-agent-handoff-child';
109
114
  const BEHAVIORAL_SKIP = HTTP_SKIP || !isFixtureAdvertised(PARENT_FIXTURE) || !isFixtureAdvertised(CHILD_FIXTURE);
110
115
 
111
- describe.skipIf(BEHAVIORAL_SKIP)('multi-agent-handoff-state-machine: behavioral 4-event causation chain (RFC 0037 §"Handoff state machine")', () => {
116
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
117
+ const GATE_WHY_2: readonly [SoftSkipKind, string] =
118
+ (!process.env.OPENWOP_BASE_URL) ? ['inapplicable', `no target: OPENWOP_BASE_URL is unset`] as const :
119
+ (!isFixtureAdvertised(PARENT_FIXTURE)) ? ['blocked', `the \`${PARENT_FIXTURE}\` fixture is not advertised`] as const :
120
+ (!isFixtureAdvertised(CHILD_FIXTURE)) ? ['blocked', `the \`${CHILD_FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
121
+
122
+ describe('multi-agent-handoff-state-machine: behavioral 4-event causation chain (RFC 0037 §"Handoff state machine")', () => {
112
123
  it('happy-path: dispatch.began → dispatch.succeeded → child.completed → output.harvested fire in causation order', async () => {
124
+ if (BEHAVIORAL_SKIP) return softSkip(...GATE_WHY_2);
113
125
  const d = await readDiscovery();
114
126
  const advertised = capabilityFamily<{ executionModel?: { [k: string]: unknown; crossHostCausation?: Record<string, unknown>; replayDeterminism?: Record<string, unknown> } }>(d, 'multiAgent')?.executionModel?.supported === true;
115
127
  if (!advertised) return softSkip('inapplicable', 'soft-skip — host honest about not implementing');
@@ -15,6 +15,7 @@ import { driver } from '../lib/driver.js';
15
15
  import { pollUntilTerminal } from '../lib/polling.js';
16
16
  import { isFixtureAdvertised } from '../lib/fixtures.js';
17
17
  import { req } from '../lib/requirement-ids.js';
18
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
18
19
 
19
20
  const WORKFLOW_ID = 'conformance-multi-node';
20
21
  const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
@@ -27,8 +28,13 @@ interface RunEvent {
27
28
  readonly sequence: number;
28
29
  }
29
30
 
30
- describe.skipIf(SKIP_NO_FIXTURE)('multi-node: conformance-multi-node fixture emits node.completed in topological order', () => {
31
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
32
+ const GATE_WHY: readonly [SoftSkipKind, string] =
33
+ (!isFixtureAdvertised(WORKFLOW_ID)) ? ['blocked', `the \`${WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
34
+
35
+ describe('multi-node: conformance-multi-node fixture emits node.completed in topological order', () => {
31
36
  it('a, b, c node.completed events arrive in DAG order by sequence', async () => {
37
+ if (SKIP_NO_FIXTURE) return softSkip(...GATE_WHY);
32
38
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
33
39
  expect(create.status).toBe(201);
34
40
  const runId = (create.json as { runId: string }).runId;
@@ -26,6 +26,7 @@ import { pollUntilTerminal } from '../lib/polling.js';
26
26
  import { isFixtureAdvertised } from '../lib/fixtures.js';
27
27
  import { SCHEMAS_DIR } from '../lib/paths.js';
28
28
  import { req } from '../lib/requirement-ids.js';
29
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
29
30
 
30
31
  const WORKFLOW_ID = 'conformance-failure';
31
32
  const SKIP_NO_FIXTURE = !isFixtureAdvertised(WORKFLOW_ID);
@@ -41,8 +42,13 @@ function nodeFailedValidator() {
41
42
  return ajv.compile(schema);
42
43
  }
43
44
 
44
- describe.skipIf(SKIP_NO_FIXTURE)('run-event-payloads.schema.json §nodeFailed — node.failed carries a structured error object', () => {
45
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
46
+ const GATE_WHY: readonly [SoftSkipKind, string] =
47
+ (!isFixtureAdvertised(WORKFLOW_ID)) ? ['blocked', `the \`${WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
48
+
49
+ describe('run-event-payloads.schema.json §nodeFailed — node.failed carries a structured error object', () => {
45
50
  it('every node.failed on the conformance-failure run validates against §nodeFailed (error: { code, message } is an object, not a string)', async () => {
51
+ if (SKIP_NO_FIXTURE) return softSkip(...GATE_WHY);
46
52
  const create = await driver.post('/v1/runs', { workflowId: WORKFLOW_ID });
47
53
  expect(create.status, req('openwop.it.node-failed-payload-shape.every-node-failed-on-the-conformance-failure-run-validates-against-nodefailed-er', 'rest-endpoints.md', 'POST /v1/runs MUST return 201 for the failing fixture')).toBe(201);
48
54
  const runId = (create.json as { runId: string }).runId;
@@ -23,12 +23,19 @@ import { driver } from '../lib/driver.js';
23
23
  import { isFixtureAdvertised } from '../lib/fixtures.js';
24
24
  import { isOrchestratorSupported } from '../lib/multi-agent-capabilities.js';
25
25
  import { req } from '../lib/requirement-ids.js';
26
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
26
27
 
27
28
  const FIXTURE = 'conformance-orchestrator-low-confidence';
28
29
  const SKIP = !isOrchestratorSupported() || !isFixtureAdvertised(FIXTURE);
29
30
 
30
- describe.skipIf(SKIP)('orchestratorConservativePath: CP-1 low-confidence suspend', () => {
31
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
32
+ const GATE_WHY: readonly [SoftSkipKind, string] =
33
+ (!isOrchestratorSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isOrchestratorSupported() is false)`] as const :
34
+ (!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
35
+
36
+ describe('orchestratorConservativePath: CP-1 low-confidence suspend', () => {
31
37
  it('supervisor below threshold suspends with reason=low-confidence; ratified decision follows after resume', async () => {
38
+ if (SKIP) return softSkip(...GATE_WHY);
32
39
  const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
33
40
  expect(create.status).toBe(201);
34
41
  const runId = (create.json as { runId: string }).runId;
@@ -28,6 +28,7 @@ import {
28
28
  isDispatchSupported,
29
29
  } from '../lib/multi-agent-capabilities.js';
30
30
  import { req } from '../lib/requirement-ids.js';
31
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
31
32
 
32
33
  const FIXTURE = 'conformance-orchestrator-dispatch';
33
34
  const SKIP =
@@ -35,8 +36,15 @@ const SKIP =
35
36
  !isDispatchSupported() ||
36
37
  !isFixtureAdvertised(FIXTURE);
37
38
 
38
- describe.skipIf(SKIP)('orchestratorDispatch: supervisor → dispatch → next-worker', () => {
39
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
40
+ const GATE_WHY: readonly [SoftSkipKind, string] =
41
+ (!isOrchestratorSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isOrchestratorSupported() is false)`] as const :
42
+ (!isDispatchSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isDispatchSupported() is false)`] as const :
43
+ (!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
44
+
45
+ describe('orchestratorDispatch: supervisor → dispatch → next-worker', () => {
39
46
  it('emits runOrchestrator.decided{next-worker} between supervisor + dispatch', async () => {
47
+ if (SKIP) return softSkip(...GATE_WHY);
40
48
  const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
41
49
  expect(create.status).toBe(201);
42
50
  const runId = (create.json as { runId: string }).runId;
@@ -21,12 +21,19 @@ import { pollUntilTerminal } from '../lib/polling.js';
21
21
  import { isFixtureAdvertised } from '../lib/fixtures.js';
22
22
  import { isOrchestratorSupported } from '../lib/multi-agent-capabilities.js';
23
23
  import { req } from '../lib/requirement-ids.js';
24
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
24
25
 
25
26
  const FIXTURE = 'conformance-orchestrator-terminate';
26
27
  const SKIP = !isOrchestratorSupported() || !isFixtureAdvertised(FIXTURE);
27
28
 
28
- describe.skipIf(SKIP)('orchestratorTermination: terminate decision → run.completed (CO-3)', () => {
29
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
30
+ const GATE_WHY: readonly [SoftSkipKind, string] =
31
+ (!isOrchestratorSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isOrchestratorSupported() is false)`] as const :
32
+ (!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
33
+
34
+ describe('orchestratorTermination: terminate decision → run.completed (CO-3)', () => {
29
35
  it('terminate is the final orchestrator decision; run completes cleanly', async () => {
36
+ if (SKIP) return softSkip(...GATE_WHY);
30
37
  const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
31
38
  expect(create.status, req('openwop.it.orchestratorTermination.terminate-is-the-final-orchestrator-decision-run-completes-cleanly', 'RFCS/0006-orchestrator.md', 'terminate is the final orchestrator decision; run completes cleanly')).toBe(201);
32
39
  const runId = (create.json as { runId: string }).runId;
@@ -17,7 +17,7 @@
17
17
  import { describe, it, expect } from 'vitest';
18
18
  import { driver } from '../lib/driver.js';
19
19
  import { isFixtureAdvertised } from '../lib/fixtures.js';
20
- import { softSkip } from '../lib/soft-skip.js';
20
+ import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
21
21
  import { pollUntil } from '../lib/polling.js';
22
22
  import { req } from '../lib/requirement-ids.js';
23
23
 
@@ -81,8 +81,13 @@ function actorDepth(s: Subject | undefined): number {
81
81
  return d;
82
82
  }
83
83
 
84
- describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC 0165 §B — gated on a host that emits owner.subject)', () => {
84
+ /** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
85
+ const GATE_WHY: readonly [SoftSkipKind, string] =
86
+ (!isFixtureAdvertised(NOOP_WORKFLOW_ID)) ? ['blocked', `the \`${NOOP_WORKFLOW_ID}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
87
+
88
+ describe('owner-subject-echo: consistency, echo, fork (RFC 0165 §B — gated on a host that emits owner.subject)', () => {
85
89
  it('subject is consistent with the owner triple (§B.2) and the actor chain is bounded (§B.5)', async () => {
90
+ if (SKIP_NO_NOOP) return softSkip(...GATE_WHY);
86
91
  const runId = await createRun();
87
92
  const snap = await snapshot(runId);
88
93
  const subject = snap.owner?.subject;
@@ -105,6 +110,7 @@ describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC
105
110
  });
106
111
 
107
112
  it('run.started echoes the snapshot subject verbatim (§B.2, RFC 0048 §C)', async () => {
113
+ if (SKIP_NO_NOOP) return softSkip(...GATE_WHY);
108
114
  const runId = await createRun();
109
115
  const snap = await snapshot(runId);
110
116
  if (snap.owner?.subject === undefined) {
@@ -122,6 +128,7 @@ describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC
122
128
  });
123
129
 
124
130
  it('a fork copies owner.tenant and owner.subject verbatim onto the child (§B.4)', async () => {
131
+ if (SKIP_NO_NOOP) return softSkip(...GATE_WHY);
125
132
  const sourceRunId = await createRun();
126
133
  const source = await snapshot(sourceRunId);
127
134
  if (source.owner?.subject === undefined) {