@openwop/openwop-conformance 2.45.0 → 2.45.1
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 +27 -0
- package/dist/spec-artifacts.lock.json +2 -2
- package/package.json +2 -2
- package/requirements.json +132 -127
- package/schemas/CORPUS-STAMP.json +9 -9
- package/src/lib/run-list-walk.ts +65 -0
- package/src/scenarios/agentConfidenceEscalation.test.ts +8 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +8 -1
- package/src/scenarios/agentMemoryRedactionContract.test.ts +8 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +8 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +8 -1
- package/src/scenarios/agentMessageReducer.test.ts +8 -1
- package/src/scenarios/agentMetadata.test.ts +8 -1
- package/src/scenarios/agentPackCatalog.test.ts +13 -5
- package/src/scenarios/agentPackExport.test.ts +8 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +17 -2
- package/src/scenarios/agentPackInstall.test.ts +8 -2
- package/src/scenarios/agentPackProvenance.test.ts +8 -1
- package/src/scenarios/agentReasoningEvents.test.ts +9 -1
- package/src/scenarios/agentReasoningStreaming.test.ts +14 -1
- package/src/scenarios/append-ordering.test.ts +7 -2
- package/src/scenarios/bulk-cancel.test.ts +11 -1
- package/src/scenarios/cap-breach.test.ts +8 -1
- package/src/scenarios/channel-ttl.test.ts +7 -1
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +8 -1
- package/src/scenarios/conversationLifecycle.test.ts +8 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +8 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +8 -1
- package/src/scenarios/cost-attribution.test.ts +17 -4
- package/src/scenarios/debugBundle.test.ts +16 -5
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -2
- package/src/scenarios/dispatch-output-mapping.test.ts +20 -4
- package/src/scenarios/dispatchLoop.test.ts +7 -1
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -2
- package/src/scenarios/highConcurrency.test.ts +11 -1
- package/src/scenarios/host-callback-declaration.test.ts +10 -1
- package/src/scenarios/identity-passthrough.test.ts +7 -1
- package/src/scenarios/interrupt-approval.test.ts +25 -5
- package/src/scenarios/interrupt-auth-required-resume.test.ts +9 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +9 -2
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +10 -2
- package/src/scenarios/interrupt-quorum-resolution.test.ts +9 -2
- package/src/scenarios/interrupt-token-matrix.test.ts +11 -3
- package/src/scenarios/interruptRace.test.ts +13 -3
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +14 -3
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +15 -3
- package/src/scenarios/multi-node-ordering.test.ts +7 -1
- package/src/scenarios/node-failed-payload-shape.test.ts +7 -1
- package/src/scenarios/orchestratorConservativePath.test.ts +8 -1
- package/src/scenarios/orchestratorDispatch.test.ts +9 -1
- package/src/scenarios/orchestratorTermination.test.ts +8 -1
- package/src/scenarios/owner-subject-echo.test.ts +9 -2
- package/src/scenarios/pause-resume.test.ts +19 -8
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +9 -2
- package/src/scenarios/prompt-end-to-end-events.test.ts +10 -2
- package/src/scenarios/replay-fork-arbitrary.test.ts +11 -4
- package/src/scenarios/restart-during-run.test.ts +7 -1
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +14 -5
- package/src/scenarios/stream-text-fixture.test.ts +8 -2
- package/src/scenarios/streamReconnect.test.ts +8 -2
- package/src/scenarios/subworkflow.test.ts +9 -1
- package/src/scenarios/v2-run-list.test.ts +14 -16
- package/src/scenarios/version-fold.test.ts +8 -2
|
@@ -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.45.
|
|
5
|
-
"corpusTag": "v2.45.
|
|
4
|
+
"version": "2.45.1",
|
|
5
|
+
"corpusTag": "v2.45.1",
|
|
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": "69464bc0e1e71ef3b543a73d4fa4f67202343d2603c80af8033f6c3619ef1b30",
|
|
11
11
|
"api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
|
|
12
|
-
"api/seams-v2.yaml": "
|
|
13
|
-
"api/v2/asyncapi.yaml": "
|
|
14
|
-
"api/v2/openapi.yaml": "
|
|
12
|
+
"api/seams-v2.yaml": "c2d7c5d68c7358229149d2552da95e32798b9c21b21a56e19d561f29eab3633e",
|
|
13
|
+
"api/v2/asyncapi.yaml": "e9c0e3cc27f8d72a6f7704de24cb81f39175b8e6bc3ae5e4ad87d46fb0be6b0c",
|
|
14
|
+
"api/v2/openapi.yaml": "622817c49d5977b51bb83685e6cb0fe27a5e087b63c3a31621959670d6829a2d",
|
|
15
15
|
"api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
|
|
16
16
|
"schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
|
|
17
17
|
"schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
"schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
|
|
205
205
|
"spec/v1/alias-detectors.json": "0b2f28808ffe0b9d98a187775038c3406bb70919747a788a6e4f866e293ae6d3",
|
|
206
206
|
"spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
|
|
207
|
-
"spec/v1/core-standard-manifest.json": "
|
|
207
|
+
"spec/v1/core-standard-manifest.json": "4aa1dbeec20dc51fb727ad85e2012af1235bac608e41836db256e7816916f898",
|
|
208
208
|
"spec/v1/deprecations.json": "a3293072ea951857395d05f0437264e88965f1e9019ff61c55bacfa9c1aaa17c",
|
|
209
209
|
"spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
|
|
210
210
|
"spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
|
|
@@ -303,9 +303,9 @@
|
|
|
303
303
|
"spec/v2/path-manifest.json": "27c2307b84376b2f22bea4e2213fc7e33ef3bf8fd1d18039f7955edb00dc9c9a",
|
|
304
304
|
"spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
|
|
305
305
|
"spec/v2/profiles.json": "180beb9ef2d161766fa23b2987e7e829851e3d316273147cf6a4a3f324befd6b",
|
|
306
|
-
"spec/v2/release.json": "
|
|
306
|
+
"spec/v2/release.json": "e6a344ecc48e172a585fafeb83631c26629373e847b144a86cc01628693ea3ad",
|
|
307
307
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
|
|
308
|
-
"spec/v2/surface-baseline.json": "
|
|
308
|
+
"spec/v2/surface-baseline.json": "b70b9f22313a99ea4853f8ffee1c4f6659b89e8cbba7317c52771fe9dfd40330"
|
|
309
309
|
},
|
|
310
|
-
"corpusCommit": "
|
|
310
|
+
"corpusCommit": "11a23de713fd4d56d9514a753d25229b029fb461"
|
|
311
311
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `GET /runs` walk the RFC 0182 run-list legs use (`v2-run-list.test.ts`).
|
|
3
|
+
*
|
|
4
|
+
* The list is newest first (`runs.md` §List), so the runs a leg just created
|
|
5
|
+
* sit on the first page. The walk therefore stops once every wanted id has
|
|
6
|
+
* been seen, then follows `nextCursor` for a bounded tail (`tailPages`, or
|
|
7
|
+
* until the list ends) so a host-minted cursor is still exercised. It never
|
|
8
|
+
* walks the whole history: on a long-lived host the conformance tenant holds
|
|
9
|
+
* thousands of runs, and a full walk outran the test timeout (openwop-app,
|
|
10
|
+
* 2026-09-29), failing a leg on the tenant's age rather than the host's
|
|
11
|
+
* behaviour.
|
|
12
|
+
*
|
|
13
|
+
* Pure: the page fetch is injected, so the walk is self-tested without a host.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type RunListPage = { runs?: unknown; nextCursor?: unknown };
|
|
17
|
+
|
|
18
|
+
export type FetchPage = (cursor: string | undefined) => Promise<{ page: RunListPage } | { reason: string }>;
|
|
19
|
+
|
|
20
|
+
export interface WalkResult {
|
|
21
|
+
/** Every runId seen, in list order. */
|
|
22
|
+
readonly ids: string[];
|
|
23
|
+
readonly pages: RunListPage[];
|
|
24
|
+
/** True when every wanted id was seen. */
|
|
25
|
+
readonly foundAll: boolean;
|
|
26
|
+
/** Why the walk stopped early (fetch failure or the page cap), if it did. */
|
|
27
|
+
readonly reason?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WalkOptions {
|
|
31
|
+
/** Ids the leg created and expects to find. */
|
|
32
|
+
readonly want: readonly string[];
|
|
33
|
+
/** Pages still followed after every wanted id is seen (default 2). */
|
|
34
|
+
readonly tailPages?: number;
|
|
35
|
+
/** Hard cap on pages fetched while searching (default 50). */
|
|
36
|
+
readonly maxPages?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function walkRunList(fetchPage: FetchPage, opts: WalkOptions): Promise<WalkResult> {
|
|
40
|
+
const tailPages = opts.tailPages ?? 2;
|
|
41
|
+
const maxPages = opts.maxPages ?? 50;
|
|
42
|
+
const wanted = new Set(opts.want);
|
|
43
|
+
const ids: string[] = [];
|
|
44
|
+
const pages: RunListPage[] = [];
|
|
45
|
+
let cursor: string | undefined;
|
|
46
|
+
let tailLeft: number | undefined; // set once every wanted id is seen
|
|
47
|
+
for (let i = 0; i < maxPages + tailPages; i++) {
|
|
48
|
+
if (tailLeft === undefined && i >= maxPages) {
|
|
49
|
+
return { ids, pages, foundAll: false, reason: `the wanted id(s) did not appear within ${maxPages} page(s)` };
|
|
50
|
+
}
|
|
51
|
+
const got = await fetchPage(cursor);
|
|
52
|
+
if ('reason' in got) return { ids, pages, foundAll: [...wanted].every((w) => ids.includes(w)), reason: got.reason };
|
|
53
|
+
pages.push(got.page);
|
|
54
|
+
for (const r of Array.isArray(got.page.runs) ? got.page.runs : []) {
|
|
55
|
+
const id = (r as { runId?: unknown }).runId;
|
|
56
|
+
if (typeof id === 'string') ids.push(id);
|
|
57
|
+
}
|
|
58
|
+
if (tailLeft === undefined && [...wanted].every((w) => ids.includes(w))) tailLeft = tailPages;
|
|
59
|
+
else if (tailLeft !== undefined) tailLeft -= 1;
|
|
60
|
+
if (typeof got.page.nextCursor !== 'string') break;
|
|
61
|
+
if (tailLeft !== undefined && tailLeft <= 0) break;
|
|
62
|
+
cursor = got.page.nextCursor;
|
|
63
|
+
}
|
|
64
|
+
return { ids, pages, foundAll: [...wanted].every((w) => ids.includes(w)) };
|
|
65
|
+
}
|
|
@@ -22,12 +22,19 @@ import { driver } from '../lib/driver.js';
|
|
|
22
22
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
23
23
|
import { isAgentSupported } from '../lib/multi-agent-capabilities.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 FIXTURE = 'conformance-agent-low-confidence';
|
|
27
28
|
const SKIP = !isAgentSupported() || !isFixtureAdvertised(FIXTURE);
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
31
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
32
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
33
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
34
|
+
|
|
35
|
+
describe('agentConfidenceEscalation: confidence < threshold → low-confidence suspend', () => {
|
|
30
36
|
it('low-confidence agent.decided suspends with reason=low-confidence and run reaches waiting-approval', async () => {
|
|
37
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
31
38
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
32
39
|
expect(create.status).toBe(201);
|
|
33
40
|
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 { hasLongTermMemory } 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-agent-memory-cross-tenant';
|
|
26
27
|
const SKIP = !hasLongTermMemory() || !isFixtureAdvertised(FIXTURE);
|
|
27
28
|
|
|
28
|
-
|
|
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
|
+
(!hasLongTermMemory()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (hasLongTermMemory() 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('agentMemoryCrossTenantIsolation: CTI-1 invariant', () => {
|
|
29
35
|
it('cross-tenant memoryRef returns empty / null — no leak across tenant boundary', async () => {
|
|
36
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
30
37
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
31
38
|
expect(create.status).toBe(201);
|
|
32
39
|
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 { hasLongTermMemory } 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-agent-memory-redaction';
|
|
26
27
|
const SKIP = !hasLongTermMemory() || !isFixtureAdvertised(FIXTURE);
|
|
27
28
|
|
|
28
|
-
|
|
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
|
+
(!hasLongTermMemory()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (hasLongTermMemory() 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('agentMemoryRedactionContract: SR-1 invariant', () => {
|
|
29
35
|
it('BYOK plaintext is redacted to [REDACTED:<secretId>] at the read surface', 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.agentMemoryRedactionContract.byok-plaintext-is-redacted-to-redacted-secretid-at-the-read-surface', 'RFCS/0004-memory-layer.md', 'BYOK plaintext is redacted to [REDACTED:<secretId>] at the read surface')).toBe(201);
|
|
32
39
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -20,12 +20,19 @@ import { pollUntilTerminal } from '../lib/polling.js';
|
|
|
20
20
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
21
21
|
import { hasLongTermMemory } from '../lib/multi-agent-capabilities.js';
|
|
22
22
|
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
23
24
|
|
|
24
25
|
const FIXTURE = 'conformance-agent-memory-roundtrip';
|
|
25
26
|
const SKIP = !hasLongTermMemory() || !isFixtureAdvertised(FIXTURE);
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
29
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
30
|
+
(!hasLongTermMemory()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (hasLongTermMemory() is false)`] as const :
|
|
31
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
32
|
+
|
|
33
|
+
describe('agentMemoryRoundTrip: write → read via MemoryAdapter', () => {
|
|
28
34
|
it('memory entries written during a run are readable via the resolved memoryRef', async () => {
|
|
35
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
29
36
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
30
37
|
expect(create.status, req('openwop.it.agentMemoryRoundTrip.memory-entries-written-during-a-run-are-readable-via-the-resolved-memoryref', 'RFCS/0004-memory-layer.md', 'memory entries written during a run are readable via the resolved memoryRef')).toBe(201);
|
|
31
38
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -19,12 +19,19 @@ import { pollUntilTerminal } from '../lib/polling.js';
|
|
|
19
19
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
20
20
|
import { hasLongTermMemory } from '../lib/multi-agent-capabilities.js';
|
|
21
21
|
import { req } from '../lib/requirement-ids.js';
|
|
22
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
22
23
|
|
|
23
24
|
const FIXTURE = 'conformance-agent-memory-ttl';
|
|
24
25
|
const SKIP = !hasLongTermMemory() || !isFixtureAdvertised(FIXTURE);
|
|
25
26
|
|
|
26
|
-
|
|
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
|
+
(!hasLongTermMemory()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (hasLongTermMemory() is false)`] as const :
|
|
30
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
31
|
+
|
|
32
|
+
describe('agentMemoryTtlExpiry: expired entries are excluded from list/get', () => {
|
|
27
33
|
it('list() excludes entries whose expiresAt is in the past', async () => {
|
|
34
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
28
35
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
29
36
|
expect(create.status).toBe(201);
|
|
30
37
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -24,12 +24,19 @@ import { pollUntilTerminal } from '../lib/polling.js';
|
|
|
24
24
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
25
25
|
import { isAgentSupported } from '../lib/multi-agent-capabilities.js';
|
|
26
26
|
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
27
28
|
|
|
28
29
|
const FIXTURE = 'conformance-message-reducer';
|
|
29
30
|
const SKIP = !isAgentSupported() || !isFixtureAdvertised(FIXTURE);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
33
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
34
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
35
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
36
|
+
|
|
37
|
+
describe('agentMessageReducer: message reducer idempotency + append-only invariant', () => {
|
|
32
38
|
it('duplicate messageId emissions fold to a single channel entry', async () => {
|
|
39
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
33
40
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
34
41
|
expect(create.status).toBe(201);
|
|
35
42
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -24,12 +24,19 @@ import { pollUntilTerminal } from '../lib/polling.js';
|
|
|
24
24
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
25
25
|
import { isAgentSupported } from '../lib/multi-agent-capabilities.js';
|
|
26
26
|
import { req } from '../lib/requirement-ids.js';
|
|
27
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
27
28
|
|
|
28
29
|
const FIXTURE = 'conformance-agent-identity';
|
|
29
30
|
const SKIP = !isAgentSupported() || !isFixtureAdvertised(FIXTURE);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
33
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
34
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
35
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
36
|
+
|
|
37
|
+
describe('agentMetadata: RunSnapshot.agent + runOrchestrator wire-shape', () => {
|
|
32
38
|
it('host populates AgentRef-shaped agent identity on RunSnapshot', async () => {
|
|
39
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
33
40
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
34
41
|
expect(create.status).toBe(201);
|
|
35
42
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -32,7 +32,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
32
32
|
import { driver } from '../lib/driver.js';
|
|
33
33
|
import { isAgentSupported } from '../lib/multi-agent-capabilities.js';
|
|
34
34
|
import { req } from '../lib/requirement-ids.js';
|
|
35
|
-
import { softSkip } from '../lib/soft-skip.js';
|
|
35
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
36
36
|
|
|
37
37
|
interface PackList {
|
|
38
38
|
packs?: Array<{
|
|
@@ -132,8 +132,13 @@ function assertAgentManifestShape(requirementId: string,
|
|
|
132
132
|
|
|
133
133
|
const SKIP = !isAgentSupported();
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
136
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
137
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
138
|
+
|
|
139
|
+
describe('core.openwop.agents.deep-research — pack catalog evidence', () => {
|
|
136
140
|
it('host pack-list includes deep-research with a well-formed AgentManifest', async () => {
|
|
141
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
137
142
|
const pack = await findPack('core.openwop.agents.deep-research');
|
|
138
143
|
if (pack === null) return softSkip('blocked', 'precondition not met — `pack === null` returned early (host doesn\'t expose /v1/packs or doesn\'t have this pack) (seam, prior step, or fixture unavailable)'); // host doesn't expose /v1/packs or doesn't have this pack
|
|
139
144
|
expect(pack.version, req('openwop.it.agentPackCatalog.host-pack-list-includes-deep-research-with-a-well-formed-agentmanifest', 'RFCS/0003-agent-packs.md', 'pack version MUST be present')).toBeDefined();
|
|
@@ -156,8 +161,9 @@ describe.skipIf(SKIP)('core.openwop.agents.deep-research — pack catalog eviden
|
|
|
156
161
|
});
|
|
157
162
|
});
|
|
158
163
|
|
|
159
|
-
describe
|
|
164
|
+
describe('core.openwop.agents.react — pack catalog evidence', () => {
|
|
160
165
|
it('host pack-list includes react with a well-formed AgentManifest', async () => {
|
|
166
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
161
167
|
const pack = await findPack('core.openwop.agents.react');
|
|
162
168
|
if (pack === null) return softSkip('blocked', 'precondition not met — `pack === null` returned early (seam, prior step, or fixture unavailable)');
|
|
163
169
|
expect(pack.version).toBeDefined();
|
|
@@ -173,8 +179,9 @@ describe.skipIf(SKIP)('core.openwop.agents.react — pack catalog evidence', ()
|
|
|
173
179
|
});
|
|
174
180
|
});
|
|
175
181
|
|
|
176
|
-
describe
|
|
182
|
+
describe('core.openwop.agents.supervisor — pack catalog evidence', () => {
|
|
177
183
|
it('host pack-list includes supervisor with a well-formed AgentManifest', async () => {
|
|
184
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
178
185
|
const pack = await findPack('core.openwop.agents.supervisor');
|
|
179
186
|
if (pack === null) return softSkip('blocked', 'precondition not met — `pack === null` returned early (seam, prior step, or fixture unavailable)');
|
|
180
187
|
expect(pack.version).toBeDefined();
|
|
@@ -194,8 +201,9 @@ describe.skipIf(SKIP)('core.openwop.agents.supervisor — pack catalog evidence'
|
|
|
194
201
|
});
|
|
195
202
|
});
|
|
196
203
|
|
|
197
|
-
describe
|
|
204
|
+
describe('agent-pack catalog summary', () => {
|
|
198
205
|
it('all three 2026-05-17 reference agent packs are catalog-reachable', async () => {
|
|
206
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
199
207
|
const names = [
|
|
200
208
|
'core.openwop.agents.deep-research',
|
|
201
209
|
'core.openwop.agents.react',
|
|
@@ -23,7 +23,7 @@ import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
|
23
23
|
import { driver } from '../lib/driver.js';
|
|
24
24
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
25
25
|
import { isAgentSupported } from '../lib/multi-agent-capabilities.js';
|
|
26
|
-
import { softSkip } from '../lib/soft-skip.js';
|
|
26
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
27
27
|
import { req } from '../lib/requirement-ids.js';
|
|
28
28
|
|
|
29
29
|
const FIXTURE = 'conformance-agent-pack-export';
|
|
@@ -49,8 +49,14 @@ function agentManifestValidator(): (doc: unknown) => { ok: boolean; errors: stri
|
|
|
49
49
|
return (doc: unknown) => ({ ok: validate(doc) as boolean, errors: ajv.errorsText(validate.errors, { separator: '; ' }) });
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
53
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
54
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
55
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
56
|
+
|
|
57
|
+
describe('agentPackExport: workspace agents project to AgentManifest', () => {
|
|
53
58
|
it('exported manifests contain required AgentManifest fields', async () => {
|
|
59
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
54
60
|
const res = await driver.get('/v1/packs/export');
|
|
55
61
|
if (res.status === 404 || res.status === 501) {
|
|
56
62
|
// Host doesn't expose pack-export over REST; treated as skip.
|
|
@@ -52,6 +52,7 @@ import { driver } from '../lib/driver.js';
|
|
|
52
52
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
53
53
|
import { isAgentSupported, hasHandoffValidation } from '../lib/multi-agent-capabilities.js';
|
|
54
54
|
import { req } from '../lib/requirement-ids.js';
|
|
55
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
55
56
|
|
|
56
57
|
const FIXTURE = 'conformance-agent-pack-handoff-schema-validation';
|
|
57
58
|
const SKIP = !isAgentSupported() || !isFixtureAdvertised(FIXTURE);
|
|
@@ -67,8 +68,18 @@ async function settle(runId: string, terminal = ['completed', 'failed', 'waiting
|
|
|
67
68
|
return undefined;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
72
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
73
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
74
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
75
|
+
|
|
76
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
77
|
+
const GATE_WHY_2: readonly [SoftSkipKind, string] =
|
|
78
|
+
(!hasHandoffValidation()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (hasHandoffValidation() is false)`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
79
|
+
|
|
80
|
+
describe('agentPackHandoffSchemaValidation: handoff schema enforcement at dispatch', () => {
|
|
71
81
|
it('HV-1a: valid task payload that matches taskSchemaRef is dispatched and completes', async () => {
|
|
82
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
72
83
|
// Valid inputs for `structured-fixture` task schema (required: text,
|
|
73
84
|
// extractionFields). No `scenario` key → the gate takes the task probe,
|
|
74
85
|
// `validateTask(inputs)` passes, the gate is ok, the no-op node runs.
|
|
@@ -88,8 +99,10 @@ describe.skipIf(SKIP)('agentPackHandoffSchemaValidation: handoff schema enforcem
|
|
|
88
99
|
|
|
89
100
|
// HV-1b / HV-1c assert a REJECTION, which only a host performing handoff
|
|
90
101
|
// validation can produce — gate on the capability that declares that behavior.
|
|
91
|
-
describe
|
|
102
|
+
describe('rejection legs (agents.manifestRuntime.handoffValidation)', () => {
|
|
92
103
|
it('HV-1b: invalid task payload (missing required field) fails before dispatch with a structured violation', async () => {
|
|
104
|
+
if (!hasHandoffValidation()) return softSkip(...GATE_WHY_2);
|
|
105
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
93
106
|
// HV-1a's inputs minus the required `extractionFields` — the paired
|
|
94
107
|
// sabotage. `validateTask` fails `required` → the run MUST fail.
|
|
95
108
|
const create = await driver.post('/v1/runs', {
|
|
@@ -121,6 +134,8 @@ describe.skipIf(SKIP)('agentPackHandoffSchemaValidation: handoff schema enforcem
|
|
|
121
134
|
});
|
|
122
135
|
|
|
123
136
|
it('HV-1c: agent return payload that fails returnSchemaRef surfaces a structured violation before persistence', async () => {
|
|
137
|
+
if (!hasHandoffValidation()) return softSkip(...GATE_WHY_2);
|
|
138
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
124
139
|
// `scenario: 'mock-return-violation'` routes to the return probe, which
|
|
125
140
|
// validates `{}` against the return schema's success-XOR-error `oneOf`
|
|
126
141
|
// (satisfies neither branch) → a return-schema violation.
|
|
@@ -24,7 +24,7 @@ import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
|
24
24
|
import { driver } from '../lib/driver.js';
|
|
25
25
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
26
26
|
import { isAgentSupported } from '../lib/multi-agent-capabilities.js';
|
|
27
|
-
import { softSkip } from '../lib/soft-skip.js';
|
|
27
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
28
28
|
import { req } from '../lib/requirement-ids.js';
|
|
29
29
|
|
|
30
30
|
const FIXTURE = 'conformance-agent-pack-install';
|
|
@@ -50,8 +50,14 @@ function agentManifestValidator(): (doc: unknown) => { ok: boolean; errors: stri
|
|
|
50
50
|
return (doc: unknown) => ({ ok: validate(doc) as boolean, errors: ajv.errorsText(validate.errors, { separator: '; ' }) });
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
54
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
55
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
56
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
57
|
+
|
|
58
|
+
describe('agentPackInstall: pack agents[] entries surface as AgentManifest', () => {
|
|
54
59
|
it('host exposes installed agent manifests with required AgentManifest fields', async () => {
|
|
60
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
55
61
|
// Host-specific pack-listing endpoint. The conformance suite probes
|
|
56
62
|
// common paths; hosts that don't expose pack listings via REST mark
|
|
57
63
|
// this scenario as skip via their own capability advertisement.
|
|
@@ -21,12 +21,19 @@ import { pollUntilTerminal } from '../lib/polling.js';
|
|
|
21
21
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
22
22
|
import { isAgentSupported } 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-agent-pack-provenance';
|
|
26
27
|
const SKIP = !isAgentSupported() || !isFixtureAdvertised(FIXTURE);
|
|
27
28
|
|
|
28
|
-
|
|
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
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() 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('agentPackProvenance: sourceManifestId survives install + run', () => {
|
|
29
35
|
it('run-level AgentRef carries sourceManifestId pointing back to the install source', async () => {
|
|
36
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
30
37
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
31
38
|
expect(create.status).toBe(201);
|
|
32
39
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
getReasoningVerbosity,
|
|
24
24
|
} 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-agent-reasoning';
|
|
28
29
|
const SKIP =
|
|
@@ -38,8 +39,15 @@ const REASONING_EVENT_TYPES = new Set([
|
|
|
38
39
|
'agent.decided',
|
|
39
40
|
]);
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
43
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
44
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
45
|
+
(getReasoningVerbosity() === 'off') ? ['inapplicable', `the host advertises reasoning verbosity \`off\`, so agent reasoning events do not apply`] as const :
|
|
46
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
47
|
+
|
|
48
|
+
describe('agentReasoningEvents: agent.* event family emission', () => {
|
|
42
49
|
it('host emits at least one canonical agent.* event during a reasoning-fixture run', async () => {
|
|
50
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
43
51
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
44
52
|
expect(create.status).toBe(201);
|
|
45
53
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
getReasoningVerbosity,
|
|
31
31
|
} from '../lib/multi-agent-capabilities.js';
|
|
32
32
|
import { req } from '../lib/requirement-ids.js';
|
|
33
|
+
import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
33
34
|
|
|
34
35
|
const FIXTURE = 'conformance-agent-reasoning-streaming';
|
|
35
36
|
/** Expected concatenation of the fixture's `streamChunks` — kept in sync
|
|
@@ -48,8 +49,16 @@ const SKIP =
|
|
|
48
49
|
getReasoningVerbosity() === 'off' ||
|
|
49
50
|
!isFixtureAdvertised(FIXTURE);
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
53
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
54
|
+
(!isAgentSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isAgentSupported() is false)`] as const :
|
|
55
|
+
(!isReasoningStreamingSupported()) ? ['inapplicable', `the host does not advertise the capability this scenario covers (isReasoningStreamingSupported() is false)`] as const :
|
|
56
|
+
(getReasoningVerbosity() === 'off') ? ['inapplicable', `the host advertises reasoning verbosity \`off\`, so agent reasoning events do not apply`] as const :
|
|
57
|
+
(!isFixtureAdvertised(FIXTURE)) ? ['blocked', `the \`${FIXTURE}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
58
|
+
|
|
59
|
+
describe('agentReasoningStreaming: RFC 0024 incremental + closing event contract', () => {
|
|
52
60
|
it('emits N agent.reasoning.delta events followed by exactly one closing agent.reasoned', async () => {
|
|
61
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
53
62
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
54
63
|
expect(create.status).toBe(201);
|
|
55
64
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -80,6 +89,7 @@ describe.skipIf(SKIP)('agentReasoningStreaming: RFC 0024 incremental + closing e
|
|
|
80
89
|
});
|
|
81
90
|
|
|
82
91
|
it('agent.reasoning.delta `sequence` starts at 0 and increments by 1 within the block', async () => {
|
|
92
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
83
93
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
84
94
|
expect(create.status).toBe(201);
|
|
85
95
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -104,6 +114,7 @@ describe.skipIf(SKIP)('agentReasoningStreaming: RFC 0024 incremental + closing e
|
|
|
104
114
|
});
|
|
105
115
|
|
|
106
116
|
it('closing agent.reasoned.reasoning is the concatenation of the deltas (authoritative)', async () => {
|
|
117
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
107
118
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
108
119
|
expect(create.status).toBe(201);
|
|
109
120
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -130,6 +141,7 @@ describe.skipIf(SKIP)('agentReasoningStreaming: RFC 0024 incremental + closing e
|
|
|
130
141
|
});
|
|
131
142
|
|
|
132
143
|
it('agentId is consistent across all streaming + closing events in a block', async () => {
|
|
144
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
133
145
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
134
146
|
expect(create.status).toBe(201);
|
|
135
147
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -158,6 +170,7 @@ describe.skipIf(SKIP)('agentReasoningStreaming: RFC 0024 incremental + closing e
|
|
|
158
170
|
});
|
|
159
171
|
|
|
160
172
|
it('all agent.reasoning.delta events arrive BEFORE the closing agent.reasoned', async () => {
|
|
173
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
161
174
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE });
|
|
162
175
|
expect(create.status).toBe(201);
|
|
163
176
|
const runId = (create.json as { runId: string }).runId;
|
|
@@ -19,7 +19,7 @@ import { driver } from '../lib/driver.js';
|
|
|
19
19
|
import { pollUntilTerminal } from '../lib/polling.js';
|
|
20
20
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
21
21
|
import { req } from '../lib/requirement-ids.js';
|
|
22
|
-
import { softSkip, blockedDespiteAssertions } from '../lib/soft-skip.js';
|
|
22
|
+
import { softSkip, blockedDespiteAssertions, type SoftSkipKind } from '../lib/soft-skip.js';
|
|
23
23
|
|
|
24
24
|
const CANDIDATES = ['conformance-append-ordering', 'conformance-multi-node'] as const;
|
|
25
25
|
const FIXTURE = CANDIDATES.find((id) => isFixtureAdvertised(id)) ?? null;
|
|
@@ -46,8 +46,13 @@ interface RunEvent {
|
|
|
46
46
|
payload?: ChannelWrittenPayload;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
50
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
51
|
+
(!FIXTURE) ? ['blocked', `none of the candidate fixtures (${CANDIDATES.join(', ')}) is advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
52
|
+
|
|
53
|
+
describe('append-ordering: folded channel reflects event sequence', () => {
|
|
50
54
|
it('append-reducer channels project entries in event-sequence order', async () => {
|
|
55
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
51
56
|
const create = await driver.post('/v1/runs', { workflowId: FIXTURE! });
|
|
52
57
|
const runId = (create.json as { runId?: string } | undefined)?.runId;
|
|
53
58
|
|
|
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
21
21
|
import { driver } from '../lib/driver.js';
|
|
22
22
|
import { isFixtureAdvertised } from '../lib/fixtures.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 CANCELLABLE = 'conformance-cancellable';
|
|
26
27
|
const NOOP = 'conformance-noop';
|
|
@@ -34,8 +35,14 @@ interface BulkResult {
|
|
|
34
35
|
error?: { code?: string; message?: string };
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
/** Why the gate below holds, as RFC 0148 §A names it (openwop#1686: a describe-level skip recorded no disposition). */
|
|
39
|
+
const GATE_WHY: readonly [SoftSkipKind, string] =
|
|
40
|
+
(!isFixtureAdvertised(CANCELLABLE)) ? ['blocked', `the \`${CANCELLABLE}\` fixture is not advertised`] as const :
|
|
41
|
+
(!isFixtureAdvertised(NOOP)) ? ['blocked', `the \`${NOOP}\` fixture is not advertised`] as const : ['blocked', 'the gate held for no named reason'] as const;
|
|
42
|
+
|
|
43
|
+
describe('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
38
44
|
it('mixed-outcome request returns per-id results in order', async () => {
|
|
45
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
39
46
|
// Spin up a long-running cancellable run + observe a known-bad id
|
|
40
47
|
// alongside it. The host MUST handle each independently.
|
|
41
48
|
const create = await driver.post('/v1/runs', {
|
|
@@ -71,6 +78,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
71
78
|
});
|
|
72
79
|
|
|
73
80
|
it('empty runIds array returns 400 validation_error', async () => {
|
|
81
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
74
82
|
const res = await driver.post('/v1/runs:bulk-cancel', { runIds: [] });
|
|
75
83
|
expect(res.status, req('openwop.it.bulk-cancel.empty-runids-array-returns-400-validation-error', 'rest-endpoints.md §Open', 'empty runIds array returns 400 validation_error')).toBe(400);
|
|
76
84
|
const body = res.json as { error?: string };
|
|
@@ -78,6 +86,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
it('oversized runIds array returns 400 with details.maxRunIds', async () => {
|
|
89
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
81
90
|
// 101 entries — exceeds the recommended 100-entry cap.
|
|
82
91
|
const ids = Array.from({ length: 101 }, (_, i) => `run-overflow-${i}`);
|
|
83
92
|
const res = await driver.post('/v1/runs:bulk-cancel', { runIds: ids });
|
|
@@ -92,6 +101,7 @@ describe.skipIf(SKIP)('bulk-cancel: POST /v1/runs:bulk-cancel', () => {
|
|
|
92
101
|
});
|
|
93
102
|
|
|
94
103
|
it('re-bulk-cancel after first cancel is idempotent', async () => {
|
|
104
|
+
if (SKIP) return softSkip(...GATE_WHY);
|
|
95
105
|
const create = await driver.post('/v1/runs', {
|
|
96
106
|
workflowId: CANCELLABLE,
|
|
97
107
|
inputs: { delaySeconds: 30 },
|