@openwop/openwop-conformance 1.136.2 → 1.136.3
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/dist/lib/profiles.js +25 -0
- package/package.json +1 -1
- package/schemas/CORPUS-STAMP.json +2 -2
- package/src/lib/feedback.ts +9 -2
- package/src/lib/memoryAttribution.ts +9 -2
- package/src/lib/profiles.ts +26 -0
- package/src/scenarios/chain-subchain-unsupported-refused.test.ts +8 -4
- package/src/scenarios/edge-condition-truthy-falsy.test.ts +8 -4
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +8 -4
- package/src/scenarios/workflow-chain-internal-flag.test.ts +8 -4
- package/src/scenarios/workflow-variable-format.test.ts +11 -6
package/dist/lib/profiles.js
CHANGED
|
@@ -542,6 +542,31 @@ export const PROFILE_FLOOR_SCENARIOS = {
|
|
|
542
542
|
// `profiles.md` §`openwop-trigger-bridge`: "The runtime conformance scenarios
|
|
543
543
|
// (`trigger-bridge-delivery.test.ts`, profile-gated) verify …" plus the shape.
|
|
544
544
|
'openwop-trigger-bridge': { required: ['trigger-bridge-shape.test.ts', 'trigger-bridge-delivery.test.ts'] },
|
|
545
|
+
// `agent-platform-profile.md` §C — the operational annex's aggregate-evidence
|
|
546
|
+
// rule: the platform claim is BACKED BY the constituent capabilities' runtime
|
|
547
|
+
// scenarios, never asserted on shape alone. S42 (2026-08-17): the profile was
|
|
548
|
+
// pushed onto `claimedProfiles` by the discovery predicate but had NO entry
|
|
549
|
+
// here, so `requirementsFor()` returned null and the verdict was always
|
|
550
|
+
// "NOT certifiable (no floor defined)" — a host on which every `agent-*`
|
|
551
|
+
// scenario passed could never certify. Floor = the §C list; replay is
|
|
552
|
+
// discovery-conditional ("or the host's declared nondeterminism").
|
|
553
|
+
'openwop-agent-platform': {
|
|
554
|
+
required: [
|
|
555
|
+
'agent-platform-aggregate-evidence.test.ts',
|
|
556
|
+
'agent-manifest-runtime.test.ts',
|
|
557
|
+
'agent-live-runtime-shape.test.ts',
|
|
558
|
+
'tool-descriptor-shape.test.ts',
|
|
559
|
+
'tool-hooks-shape.test.ts',
|
|
560
|
+
'safefetch-behavior.test.ts',
|
|
561
|
+
'provider-usage.test.ts',
|
|
562
|
+
'prompt-list-and-fetch.test.ts',
|
|
563
|
+
'memory-capability-model-shape.test.ts',
|
|
564
|
+
'feedback-capability-shape.test.ts',
|
|
565
|
+
],
|
|
566
|
+
conditional: [
|
|
567
|
+
{ path: 'replay.modes', includes: 'replay', required: ['replayDeterminism.test.ts'] },
|
|
568
|
+
],
|
|
569
|
+
},
|
|
545
570
|
// `profiles.md` §`openwop-replay-fork`: "This profile gates
|
|
546
571
|
// `replayDeterminism.test.ts` and `replay-fork.test.ts` … the conformance
|
|
547
572
|
// scenarios pass on whichever mode the host advertises." Discovery-conditional
|
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.136.
|
|
4
|
-
"corpusCommit": "
|
|
3
|
+
"suiteVersion": "1.136.3",
|
|
4
|
+
"corpusCommit": "54f29548af7c1b388553b982a6819ecd7d7ae4d9"
|
|
5
5
|
}
|
package/src/lib/feedback.ts
CHANGED
|
@@ -24,9 +24,16 @@ const SEED_FIXTURE = 'conformance-noop';
|
|
|
24
24
|
|
|
25
25
|
/** Seeds a run via the basic `conformance-noop` fixture; null (soft-skip)
|
|
26
26
|
* when the fixture isn't advertised or creation fails. */
|
|
27
|
-
export async function seedRun(
|
|
27
|
+
export async function seedRun(_label: string): Promise<string | null> {
|
|
28
28
|
if (!isFixtureAdvertised(SEED_FIXTURE)) return null;
|
|
29
|
-
|
|
29
|
+
// S41 (2026-08-17): the argument used to be sent as `tenantId` on POST /v1/runs. It was
|
|
30
|
+
// a fabricated label (`mem-attr-emit`, `feedback-cti`, …), and a host that enforces
|
|
31
|
+
// "tenantId MUST match the principal's accessible workspaces" answers 403 — MyndHyve did,
|
|
32
|
+
// which left every scenario on this helper `blocked` and kept `openwop-memory` from
|
|
33
|
+
// certifying. No caller ever needed a SECOND tenant: every leg reads back its own run.
|
|
34
|
+
// The tenant is the credential's own tenant (the host defaults it from the API key —
|
|
35
|
+
// rest-endpoints.md), which is what "a run the bearer provably owns" means.
|
|
36
|
+
const r = await driver.post('/v1/runs', { workflowId: SEED_FIXTURE, inputs: {} });
|
|
30
37
|
if (r.status !== 200 && r.status !== 201) return null;
|
|
31
38
|
return (r.json as { runId?: string } | undefined)?.runId ?? null;
|
|
32
39
|
}
|
|
@@ -24,9 +24,16 @@ const SEED_FIXTURE = 'conformance-noop';
|
|
|
24
24
|
|
|
25
25
|
/** Seeds a basic run (the host writes a run-summary on completion); null
|
|
26
26
|
* (soft-skip) when the fixture isn't advertised or creation fails. */
|
|
27
|
-
export async function seedRun(
|
|
27
|
+
export async function seedRun(_label: string): Promise<string | null> {
|
|
28
28
|
if (!isFixtureAdvertised(SEED_FIXTURE)) return null;
|
|
29
|
-
|
|
29
|
+
// S41 (2026-08-17): the argument used to be sent as `tenantId` on POST /v1/runs. It was
|
|
30
|
+
// a fabricated label (`mem-attr-emit`, `feedback-cti`, …), and a host that enforces
|
|
31
|
+
// "tenantId MUST match the principal's accessible workspaces" answers 403 — MyndHyve did,
|
|
32
|
+
// which left every scenario on this helper `blocked` and kept `openwop-memory` from
|
|
33
|
+
// certifying. No caller ever needed a SECOND tenant: every leg reads back its own run.
|
|
34
|
+
// The tenant is the credential's own tenant (the host defaults it from the API key —
|
|
35
|
+
// rest-endpoints.md), which is what "a run the bearer provably owns" means.
|
|
36
|
+
const r = await driver.post('/v1/runs', { workflowId: SEED_FIXTURE, inputs: {} });
|
|
30
37
|
if (r.status !== 200 && r.status !== 201) return null;
|
|
31
38
|
return (r.json as { runId?: string } | undefined)?.runId ?? null;
|
|
32
39
|
}
|
package/src/lib/profiles.ts
CHANGED
|
@@ -621,6 +621,32 @@ export const PROFILE_FLOOR_SCENARIOS: Readonly<Record<string, ProfileFloor>> = {
|
|
|
621
621
|
// (`trigger-bridge-delivery.test.ts`, profile-gated) verify …" plus the shape.
|
|
622
622
|
'openwop-trigger-bridge': { required: ['trigger-bridge-shape.test.ts', 'trigger-bridge-delivery.test.ts'] },
|
|
623
623
|
|
|
624
|
+
// `agent-platform-profile.md` §C — the operational annex's aggregate-evidence
|
|
625
|
+
// rule: the platform claim is BACKED BY the constituent capabilities' runtime
|
|
626
|
+
// scenarios, never asserted on shape alone. S42 (2026-08-17): the profile was
|
|
627
|
+
// pushed onto `claimedProfiles` by the discovery predicate but had NO entry
|
|
628
|
+
// here, so `requirementsFor()` returned null and the verdict was always
|
|
629
|
+
// "NOT certifiable (no floor defined)" — a host on which every `agent-*`
|
|
630
|
+
// scenario passed could never certify. Floor = the §C list; replay is
|
|
631
|
+
// discovery-conditional ("or the host's declared nondeterminism").
|
|
632
|
+
'openwop-agent-platform': {
|
|
633
|
+
required: [
|
|
634
|
+
'agent-platform-aggregate-evidence.test.ts',
|
|
635
|
+
'agent-manifest-runtime.test.ts',
|
|
636
|
+
'agent-live-runtime-shape.test.ts',
|
|
637
|
+
'tool-descriptor-shape.test.ts',
|
|
638
|
+
'tool-hooks-shape.test.ts',
|
|
639
|
+
'safefetch-behavior.test.ts',
|
|
640
|
+
'provider-usage.test.ts',
|
|
641
|
+
'prompt-list-and-fetch.test.ts',
|
|
642
|
+
'memory-capability-model-shape.test.ts',
|
|
643
|
+
'feedback-capability-shape.test.ts',
|
|
644
|
+
],
|
|
645
|
+
conditional: [
|
|
646
|
+
{ path: 'replay.modes', includes: 'replay', required: ['replayDeterminism.test.ts'] },
|
|
647
|
+
],
|
|
648
|
+
},
|
|
649
|
+
|
|
624
650
|
// `profiles.md` §`openwop-replay-fork`: "This profile gates
|
|
625
651
|
// `replayDeterminism.test.ts` and `replay-fork.test.ts` … the conformance
|
|
626
652
|
// scenarios pass on whichever mode the host advertises." Discovery-conditional
|
|
@@ -23,16 +23,20 @@
|
|
|
23
23
|
import { describe, it, expect } from 'vitest';
|
|
24
24
|
import { readFileSync } from 'node:fs';
|
|
25
25
|
import { join } from 'node:path';
|
|
26
|
-
import {
|
|
26
|
+
import { V1_DIR } from '../lib/paths.js';
|
|
27
27
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
28
28
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
29
29
|
|
|
30
30
|
const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
|
|
31
|
-
|
|
31
|
+
// S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
|
|
32
|
+
// from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
|
|
33
|
+
// MyndHyve's bundle for a reason that had nothing to do with the host. Prose legs are
|
|
34
|
+
// repo-layout only: `null` in the published layout and skipped, never thrown.
|
|
35
|
+
const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
|
|
32
36
|
|
|
33
37
|
describe('chain-subchain-unsupported-refused §A: corpus contract (RFC 0133, always-on)', () => {
|
|
34
|
-
it('spec corpus pins sub_chain_unsupported as a 422 refusal (never flatten)', () => {
|
|
35
|
-
const doc = readFileSync(CHAIN_DOC, 'utf8');
|
|
38
|
+
it.skipIf(CHAIN_DOC === null)('spec corpus pins sub_chain_unsupported as a 422 refusal (never flatten)', () => {
|
|
39
|
+
const doc = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
36
40
|
expect(doc.includes('sub_chain_unsupported'), cite('§Error codes', 'the code MUST be registered')).toBe(true);
|
|
37
41
|
// The normative refusal-not-flatten rule MUST appear in the composition section.
|
|
38
42
|
expect(
|
|
@@ -22,14 +22,18 @@ import { readFileSync } from 'node:fs';
|
|
|
22
22
|
import { join } from 'node:path';
|
|
23
23
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
24
24
|
import addFormats from 'ajv-formats';
|
|
25
|
-
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
25
|
+
import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
|
|
26
26
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
27
27
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
28
28
|
|
|
29
29
|
const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
|
|
30
30
|
const WORKFLOW_DEF = join(SCHEMAS_DIR, 'workflow-definition.schema.json');
|
|
31
31
|
const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
|
|
32
|
-
|
|
32
|
+
// S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
|
|
33
|
+
// from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
|
|
34
|
+
// MyndHyve's bundle for a reason that had nothing to do with the host. Prose legs are
|
|
35
|
+
// repo-layout only: `null` in the published layout and skipped, never thrown.
|
|
36
|
+
const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
|
|
33
37
|
|
|
34
38
|
function loadSchema(path: string): Record<string, unknown> {
|
|
35
39
|
return JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>;
|
|
@@ -77,8 +81,8 @@ describe('edge-condition-truthy-falsy §A: corpus (RFC 0134, always-on)', () =>
|
|
|
77
81
|
expect(validate(pack), cite('§EdgeCondition', `truthy/falsy edges validate: ${ajv.errorsText(validate.errors)}`)).toBe(true);
|
|
78
82
|
});
|
|
79
83
|
|
|
80
|
-
it('the spec documents the no-`right` + required-`left` truthy/falsy semantics', () => {
|
|
81
|
-
const doc = readFileSync(CHAIN_DOC, 'utf8');
|
|
84
|
+
it.skipIf(CHAIN_DOC === null)('the spec documents the no-`right` + required-`left` truthy/falsy semantics', () => {
|
|
85
|
+
const doc = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
82
86
|
expect(doc.includes('truthy'), cite('§Edge-condition operators', 'documents truthy')).toBe(true);
|
|
83
87
|
expect(
|
|
84
88
|
/truthy[\s\S]{0,400}(no|without).{0,20}`?right`?/i.test(doc) || /(no|without).{0,20}`?right`?[\s\S]{0,400}truthy/i.test(doc),
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
import { describe, it, expect } from 'vitest';
|
|
41
41
|
import { readFileSync } from 'node:fs';
|
|
42
42
|
import { join } from 'node:path';
|
|
43
|
-
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
43
|
+
import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
|
|
44
44
|
import { driver } from '../lib/driver.js';
|
|
45
45
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
46
46
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
@@ -53,7 +53,11 @@ import {
|
|
|
53
53
|
|
|
54
54
|
const CAPS = join(SCHEMAS_DIR, 'capabilities.schema.json');
|
|
55
55
|
const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
|
|
56
|
-
|
|
56
|
+
// S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
|
|
57
|
+
// from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
|
|
58
|
+
// MyndHyve's bundle for a reason that had nothing to do with the host. Prose legs are
|
|
59
|
+
// repo-layout only: `null` in the published layout and skipped, never thrown.
|
|
60
|
+
const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
|
|
57
61
|
|
|
58
62
|
/** Spec-cited assertion message. Unlike `driver.describe`, this does NOT load
|
|
59
63
|
* env, so it is safe in always-on server-free legs (no OPENWOP_BASE_URL). */
|
|
@@ -240,8 +244,8 @@ describe('workflow-chain-deferred: schema + spec surface (always-on, server-free
|
|
|
240
244
|
).toBe(true);
|
|
241
245
|
});
|
|
242
246
|
|
|
243
|
-
it('the spec pins the error code + the per-run credentialRef (not plaintext) supply shape', () => {
|
|
244
|
-
const spec = readFileSync(CHAIN_DOC, 'utf8');
|
|
247
|
+
it.skipIf(CHAIN_DOC === null)('the spec pins the error code + the per-run credentialRef (not plaintext) supply shape', () => {
|
|
248
|
+
const spec = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
245
249
|
expect(spec.includes('sensitive_param_not_deferrable'), 'error code MUST be documented').toBe(true);
|
|
246
250
|
expect(
|
|
247
251
|
spec.includes('credentialRef'),
|
|
@@ -20,11 +20,15 @@ import { readFileSync } from 'node:fs';
|
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
22
22
|
import addFormats from 'ajv-formats';
|
|
23
|
-
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
23
|
+
import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
|
|
24
24
|
|
|
25
25
|
const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
|
|
26
26
|
const MANIFEST = join(SCHEMAS_DIR, 'workflow-chain-pack-manifest.schema.json');
|
|
27
|
-
|
|
27
|
+
// S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
|
|
28
|
+
// from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
|
|
29
|
+
// MyndHyve's bundle for a reason that had nothing to do with the host. Prose legs are
|
|
30
|
+
// repo-layout only: `null` in the published layout and skipped, never thrown.
|
|
31
|
+
const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
|
|
28
32
|
|
|
29
33
|
function packWith(internal: unknown): Record<string, unknown> {
|
|
30
34
|
return {
|
|
@@ -65,8 +69,8 @@ describe('workflow-chain-internal-flag §A: corpus (RFC 0135, always-on)', () =>
|
|
|
65
69
|
expect(validate(packWith('yes')), cite('§WorkflowChain', 'non-boolean internal rejected')).toBe(false);
|
|
66
70
|
});
|
|
67
71
|
|
|
68
|
-
it('the spec documents the MUST-omit-from-default-gallery + not-an-authz-boundary rules', () => {
|
|
69
|
-
const doc = readFileSync(CHAIN_DOC, 'utf8');
|
|
72
|
+
it.skipIf(CHAIN_DOC === null)('the spec documents the MUST-omit-from-default-gallery + not-an-authz-boundary rules', () => {
|
|
73
|
+
const doc = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
70
74
|
expect(doc.includes('Chain visibility (RFC 0135)'), cite('§Chain visibility', 'section present')).toBe(true);
|
|
71
75
|
expect(
|
|
72
76
|
/internal[\s\S]{0,600}MUST omit/i.test(doc),
|
|
@@ -43,7 +43,7 @@ import { readFileSync } from 'node:fs';
|
|
|
43
43
|
import { join } from 'node:path';
|
|
44
44
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
45
45
|
import addFormats from 'ajv-formats';
|
|
46
|
-
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
46
|
+
import { SCHEMAS_DIR, V1_DIR } from '../lib/paths.js';
|
|
47
47
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
48
48
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
49
49
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
@@ -51,7 +51,12 @@ import { driver } from '../lib/driver.js';
|
|
|
51
51
|
|
|
52
52
|
const cite = (section: string, requirement: string): string => `${section} — ${requirement}`;
|
|
53
53
|
const WORKFLOW_DEF = join(SCHEMAS_DIR, 'workflow-definition.schema.json');
|
|
54
|
-
|
|
54
|
+
// S38 (2026-08-17): `spec/` is NOT in the published package (`files`), so a path built
|
|
55
|
+
// from SCHEMAS_DIR/../spec ENOENTs for every npm consumer — five always-on legs reddened
|
|
56
|
+
// MyndHyve's bundle for a reason that had nothing to do with the host. Prose legs are
|
|
57
|
+
// repo-layout only: `null` in the published layout and skipped, never thrown.
|
|
58
|
+
const CHAIN_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, 'workflow-chain-packs.md');
|
|
59
|
+
const RFC_DOC: string | null = V1_DIR === null ? null : join(V1_DIR, '..', '..', 'RFCS', '0136-workflow-variable-format.md');
|
|
55
60
|
|
|
56
61
|
function loadSchema(path: string): Record<string, unknown> {
|
|
57
62
|
return JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>;
|
|
@@ -115,8 +120,8 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
115
120
|
).toBe(true);
|
|
116
121
|
});
|
|
117
122
|
|
|
118
|
-
it('A4 — chain-pack spec documents deferred-mode `format` propagation as mode-scoped', () => {
|
|
119
|
-
const doc = readFileSync(CHAIN_DOC, 'utf8');
|
|
123
|
+
it.skipIf(CHAIN_DOC === null)('A4 — chain-pack spec documents deferred-mode `format` propagation as mode-scoped', () => {
|
|
124
|
+
const doc = readFileSync(CHAIN_DOC as string, 'utf8');
|
|
120
125
|
const step1 = doc.slice(doc.indexOf('Materialize parameters as variables'));
|
|
121
126
|
expect(step1.length > 0, cite('§Deferred-parameter expansion', 'step 1 present')).toBe(true);
|
|
122
127
|
expect(
|
|
@@ -129,8 +134,8 @@ describe('workflow-variable-format §A: corpus (RFC 0136, always-on)', () => {
|
|
|
129
134
|
).toBe(true);
|
|
130
135
|
});
|
|
131
136
|
|
|
132
|
-
it('A5 — the RFC forbids `format` participating in a `configurable` validation decision', () => {
|
|
133
|
-
const rfc = readFileSync(
|
|
137
|
+
it.skipIf(RFC_DOC === null)('A5 — the RFC forbids `format` participating in a `configurable` validation decision', () => {
|
|
138
|
+
const rfc = readFileSync(RFC_DOC as string, 'utf8');
|
|
134
139
|
expect(
|
|
135
140
|
/configurableSchema/.test(rfc),
|
|
136
141
|
cite('RFC 0136', 'names the configurableSchema propagation path'),
|