@openwop/openwop-conformance 1.72.0 → 1.72.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.
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conformance-workflow-variable-format-advisory",
|
|
3
|
+
"name": "Conformance: WorkflowVariable.format is advisory (RFC 0136)",
|
|
4
|
+
"version": "1.0",
|
|
5
|
+
"description": "RFC 0136 requirement 3 witness. A single-node workflow whose variable `recipientEmail` declares `format: \"email\"` but carries an off-format `defaultValue` (\"not-an-email\"). `format` is an ADVISORY presentational hint, never a validation constraint — a host MUST NOT reject the definition or fail the run on the value/format mismatch. The scenario runs this fixture via POST /v1/runs (the portable pre-registered-workflow pattern, no host-specific create endpoint) and asserts the run reaches `completed`; a host that validates a variable value against its advisory `format` reds. Vendored (not created via POST /v1/workflows) because registration is 'POST /v1/workflows or equivalent' per capabilities.md — the create path is not a mandated portable surface.",
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "n1",
|
|
9
|
+
"typeId": "core.identity",
|
|
10
|
+
"name": "Noop (format-advisory witness)",
|
|
11
|
+
"position": { "x": 0, "y": 0 },
|
|
12
|
+
"config": {},
|
|
13
|
+
"inputs": {}
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"edges": [],
|
|
17
|
+
"triggers": [
|
|
18
|
+
{ "id": "manual", "type": "manual", "enabled": true }
|
|
19
|
+
],
|
|
20
|
+
"variables": [
|
|
21
|
+
{ "name": "recipientEmail", "type": "string", "format": "email", "defaultValue": "not-an-email" }
|
|
22
|
+
],
|
|
23
|
+
"metadata": {
|
|
24
|
+
"tags": ["conformance", "rfc-0136", "workflow-variable-format"]
|
|
25
|
+
},
|
|
26
|
+
"settings": { "timeout": 5000 }
|
|
27
|
+
}
|
package/fixtures.md
CHANGED
|
@@ -69,6 +69,7 @@ All fixtures MUST advertise:
|
|
|
69
69
|
| Agent Pack Export | `conformance-agent-pack-export` | Phase 2 — workspace agents project to AgentManifest at `GET /v1/packs/export` | `completed` | ≤ 5s |
|
|
70
70
|
| Agent Pack Provenance | `conformance-agent-pack-provenance` | Phase 2 — `sourceManifestId` provenance round-trip | `completed` | ≤ 10s |
|
|
71
71
|
| Agent Pack Handoff Schema Validation | `conformance-agent-pack-handoff-schema-validation` | Phase 2 / HV-1 — host validates dispatch payloads against `handoff.taskSchemaRef` AND return payloads against `handoff.returnSchemaRef` per RFC 0003 §D. Three branches: valid-task → `completed`; invalid-task → `failed` with structured violation; mock-return-violation → violation surfaced before persistence. | varies by scenario | ≤ 5s |
|
|
72
|
+
| WorkflowVariable format advisory | `conformance-workflow-variable-format-advisory` | RFC 0136 req 3 — a variable declares `format: "email"` with an off-format `defaultValue`; `format` is advisory, so the run MUST complete (a value/format mismatch MUST NOT fail the run). Run via `POST /v1/runs` (portable pre-registered pattern). `workflow-variable-format.test.ts` leg B2. | `completed` | ≤ 5s |
|
|
72
73
|
| Dispatch Input Mapping | `conformance-dispatch-input-mapping` | RFC 0022 §A / HVMAP-1a — host honors `inputMapping` on `core.dispatch`. Capability-gated on `capabilities.agents.dispatchMapping`. | `completed` | ≤ 5s |
|
|
73
74
|
| Dispatch Output Mapping | `conformance-dispatch-output-mapping` | RFC 0022 §A / HVMAP-1b — host harvests child variables via `outputMapping` on `core.dispatch`. Capability-gated on `capabilities.agents.dispatchMapping`. | `completed` | ≤ 5s |
|
|
74
75
|
| Dispatch Cross-Worker Handoff | `conformance-dispatch-cross-worker-handoff` | RFC 0022 §A / HVMAP-1c — sequential fan-out: child-a writes via `perWorkerOutputMappings`, child-b reads via `perWorkerInputMappings`. Capability-gated on `capabilities.agents.dispatchMapping`. | `completed` | ≤ 10s |
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Provenance of this vendored schemas/ copy. See conformance/README.md \u00a7\"Resolving the contract\". Compare against the stamp in your installed @openwop/openwop-conformance to detect a stale hand-copied contract.",
|
|
3
|
-
"suiteVersion": "1.72.
|
|
4
|
-
"corpusCommit": "
|
|
3
|
+
"suiteVersion": "1.72.2",
|
|
4
|
+
"corpusCommit": "e58d3553617279966cb010b68d7ac4caf8110ec7"
|
|
5
5
|
}
|
|
@@ -9,15 +9,29 @@
|
|
|
9
9
|
* an unknown hint into a hard `POST /v1/workflows` failure); `format` composes with
|
|
10
10
|
* `sensitive` on one variable; and `workflow-chain-packs.md` documents the
|
|
11
11
|
* deferred-mode propagation as a mode-scoped MUST.
|
|
12
|
-
* B. Capability-gated host
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* B. Capability-gated host legs (`workflowChainPacks.deferredParameters.supported`) —
|
|
13
|
+
* real behavioral drives, not advert re-checks:
|
|
14
|
+
* B1 drives the RFC 0124 deferred-expand seam
|
|
15
|
+
* (`POST /v1/host/sample/chain/deferred-expand`) and asserts the minted
|
|
16
|
+
* `variables[]` carry `format` per the "present iff minted" contract — a string
|
|
17
|
+
* param's `format` copied verbatim (req 7), an unrecognised value copied too
|
|
18
|
+
* (req 2), a non-string param minting no `format` (req 1). Soft-skips (404 /
|
|
19
|
+
* absent `variables`) on a host that advertises the flag but hasn't wired the
|
|
20
|
+
* seam's `variables[]` extension.
|
|
21
|
+
* B2 runs the vendored fixture `conformance-workflow-variable-format-advisory`
|
|
22
|
+
* (a workflow whose variable declares `format:"email"` with an off-format
|
|
23
|
+
* `defaultValue`) via `POST /v1/runs` — the portable pre-registered pattern, NOT a
|
|
24
|
+
* `POST /v1/workflows` create (registration is "POST /v1/workflows or equivalent",
|
|
25
|
+
* capabilities.md, so the create path is not a mandated portable surface). It asserts
|
|
26
|
+
* the run COMPLETES — a value/format mismatch MUST NOT fail the run (requirement 3,
|
|
27
|
+
* `format` advisory not validating). Gated on fixture advertisement; a
|
|
28
|
+
* format-validating host reds.
|
|
17
29
|
*
|
|
18
30
|
* NON-VACUITY: leg A2 is the one that would silently pass if `format` were declared as an
|
|
19
31
|
* enum — it asserts that a value OUTSIDE the recognised table validates. Sabotage: adding
|
|
20
|
-
* `"enum": [...]` to the schema property reds A2 alone and leaves A1 green.
|
|
32
|
+
* `"enum": [...]` to the schema property reds A2 alone and leaves A1 green. B1 reds on a
|
|
33
|
+
* host that mints the variable but drops `format` (step-4-unwired); B2 reds on a host that
|
|
34
|
+
* validates a variable value against its advisory `format`.
|
|
21
35
|
*
|
|
22
36
|
* @see schemas/workflow-definition.schema.json §WorkflowVariable
|
|
23
37
|
* @see spec/v1/workflow-chain-packs.md §"Deferred-parameter expansion (RFC 0124)" step 1
|
|
@@ -32,6 +46,8 @@ import addFormats from 'ajv-formats';
|
|
|
32
46
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
33
47
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
34
48
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
49
|
+
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
50
|
+
import { driver } from '../lib/driver.js';
|
|
35
51
|
|
|
36
52
|
const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
|
|
37
53
|
const WORKFLOW_DEF = join(SCHEMAS_DIR, 'workflow-definition.schema.json');
|
|
@@ -132,24 +148,86 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
132
148
|
});
|
|
133
149
|
|
|
134
150
|
describe('workflow-variable-format §B: host behaviour (RFC 0136, capability-gated)', () => {
|
|
135
|
-
|
|
136
|
-
|
|
151
|
+
// RFC 0124's deferred-expand witness seam (referenced from capabilities.md §deferredParameters).
|
|
152
|
+
const DEFERRED_EXPAND_SEAM = '/v1/host/sample/chain/deferred-expand';
|
|
153
|
+
|
|
154
|
+
// Gate open only when a reachable host advertises deferredParameters. A missing base URL
|
|
155
|
+
// (server-free) makes the discovery fetch throw — treat that as "no host" and skip, so
|
|
156
|
+
// these host legs never error the full-suite run; the server-free gate covers §A.
|
|
157
|
+
async function deferredParamsGateOpen(): Promise<boolean> {
|
|
158
|
+
let wcp: { deferredParameters?: { supported?: boolean } } | undefined;
|
|
159
|
+
try {
|
|
160
|
+
wcp = await readCapabilityFamily<{ deferredParameters?: { supported?: boolean } }>('workflowChainPacks');
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
137
164
|
const deferred = wcp?.deferredParameters?.supported === true;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
return behaviorGate('workflowChainPacks.deferredParameters.supported', deferred);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
it('B1 — deferred expansion mints `format` onto the WorkflowVariable: string copied (req 7), unknown copied (req 2), non-string omits it (req 1)', async () => {
|
|
169
|
+
if (!(await deferredParamsGateOpen())) return;
|
|
170
|
+
|
|
171
|
+
// Drive the RFC 0124 deferred-expand seam with a chain whose parameters exercise all
|
|
172
|
+
// three propagation rules in one expansion; assert the minted variables[] carry (or omit)
|
|
173
|
+
// `format` per the "present iff minted" contract.
|
|
174
|
+
const res = await driver.post(DEFERRED_EXPAND_SEAM, {
|
|
175
|
+
chain: {
|
|
176
|
+
parameters: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
properties: {
|
|
179
|
+
email: { type: 'string', format: 'email' },
|
|
180
|
+
note: { type: 'string', format: 'vendor.acme.freeform' },
|
|
181
|
+
count: { type: 'number' },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
// A host advertising deferredParameters but not yet serving the variables[]-returning
|
|
187
|
+
// seam extension soft-skips (404), as does a host with the seam disabled in this boot.
|
|
188
|
+
if (res.status === 404) return;
|
|
189
|
+
expect(res.status, cite('§Deferred-parameter expansion', 'the deferred-expand seam returns 200')).toBe(200);
|
|
190
|
+
const variables = (res.json as { variables?: Array<{ name: string; type?: string; format?: string }> }).variables;
|
|
191
|
+
if (!Array.isArray(variables)) return; // seam present but not returning variables[] yet — soft-skip
|
|
192
|
+
const fmt = (n: string): string | undefined => variables.find((v) => v.name === n)?.format;
|
|
193
|
+
|
|
194
|
+
expect(fmt('email'), cite('§WorkflowVariable', 'req 7: a string param\'s recognised `format` is minted verbatim')).toBe('email');
|
|
195
|
+
expect(fmt('note'), cite('§WorkflowVariable', 'req 2: an UNRECOGNISED `format` is minted verbatim, never dropped')).toBe('vendor.acme.freeform');
|
|
196
|
+
expect(fmt('count'), cite('§WorkflowVariable', 'req 1: a NON-STRING param mints no `format`')).toBeUndefined();
|
|
144
197
|
});
|
|
145
198
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
199
|
+
const B2_FIXTURE = 'conformance-workflow-variable-format-advisory';
|
|
200
|
+
|
|
201
|
+
it('B2 — a run whose variable value does not match its declared `format` is accepted and completes (req 3: `format` is advisory, not validating)', async () => {
|
|
202
|
+
// Portable pre-registered-workflow pattern (cf. agentPackHandoffSchemaValidation): the
|
|
203
|
+
// fixture `conformance-workflow-variable-format-advisory` carries a variable declaring
|
|
204
|
+
// `format:"email"` with an off-format `defaultValue`, run via POST /v1/runs — NO create
|
|
205
|
+
// endpoint, since registration is "POST /v1/workflows or equivalent" (capabilities.md)
|
|
206
|
+
// and the create path is not a mandated portable surface. Gate on fixture advertisement:
|
|
207
|
+
// req 3 is universal (not deferred-mode-specific), so it rides the host loading the fixture.
|
|
208
|
+
if (!isFixtureAdvertised(B2_FIXTURE)) return;
|
|
209
|
+
|
|
210
|
+
const run = await driver.post('/v1/runs', { workflowId: B2_FIXTURE });
|
|
211
|
+
if (run.status === 404) return; // no run surface — soft-skip
|
|
212
|
+
expect(
|
|
213
|
+
[200, 201],
|
|
214
|
+
cite('POST /v1/runs', 'req 3: a run whose variable value violates its advisory `format` is accepted, not rejected'),
|
|
215
|
+
).toContain(run.status);
|
|
216
|
+
const runId = (run.json as { runId: string }).runId;
|
|
217
|
+
|
|
218
|
+
let snap: { status: string } | undefined;
|
|
219
|
+
for (let i = 0; i < 40; i++) {
|
|
220
|
+
const r = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
|
|
221
|
+
const body = r.json as { status: string };
|
|
222
|
+
if (['completed', 'failed', 'waiting-approval'].includes(body.status)) {
|
|
223
|
+
snap = body;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
227
|
+
}
|
|
228
|
+
expect(
|
|
229
|
+
snap?.status,
|
|
230
|
+
cite('§WorkflowVariable', 'req 3: the run COMPLETES — `format` is advisory, a value mismatch MUST NOT fail the run'),
|
|
231
|
+
).toBe('completed');
|
|
154
232
|
});
|
|
155
233
|
});
|