@openwop/openwop-conformance 1.136.2 → 1.136.4

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/cli.js CHANGED
@@ -12,6 +12,7 @@
12
12
  * openwop-conformance --filter discovery # category filter
13
13
  * openwop-conformance --base-url ... --api-key ... --filter "interrupt|cancellation"
14
14
  * openwop-conformance --base-url ... --api-key ... --certify out.json # RFC 0089 bundle
15
+ * openwop-conformance --base-url ... --api-key ... --max-workers 4 # cap parallel scenario files
15
16
  *
16
17
  * Environment variables override flags (per the conformance harness's
17
18
  * existing convention):
@@ -46,6 +47,7 @@ function parseArgs(argv) {
46
47
  let implVersion;
47
48
  let certify;
48
49
  let bundleVersion = '1';
50
+ let maxWorkers = parseMaxWorkers(process.env.OPENWOP_MAX_WORKERS, 'OPENWOP_MAX_WORKERS');
49
51
  for (let i = 0; i < argv.length; i++) {
50
52
  const arg = argv[i] ?? '';
51
53
  if (arg === '-h' || arg === '--help') {
@@ -99,13 +101,42 @@ function parseArgs(argv) {
99
101
  case '--certify':
100
102
  certify = nextValue();
101
103
  break;
104
+ case '--max-workers':
105
+ maxWorkers = parseMaxWorkers(nextValue(), '--max-workers');
106
+ break;
102
107
  default:
103
108
  if (arg.startsWith('-')) {
104
109
  // Unknown flag — pass through to vitest by ignoring here.
105
110
  }
106
111
  }
107
112
  }
108
- return { baseUrl, apiKey, offline, filter, help, impl, implVersion, certify, bundleVersion };
113
+ return {
114
+ baseUrl,
115
+ apiKey,
116
+ offline,
117
+ filter,
118
+ help,
119
+ impl,
120
+ implVersion,
121
+ certify,
122
+ bundleVersion,
123
+ maxWorkers,
124
+ };
125
+ }
126
+ /** Parse a `--max-workers` / `OPENWOP_MAX_WORKERS` value: a positive integer, else exit 2. */
127
+ function parseMaxWorkers(raw, source) {
128
+ if (raw === undefined || raw === '')
129
+ return undefined;
130
+ const n = Number(raw);
131
+ if (!Number.isInteger(n) || n < 1) {
132
+ process.stderr.write(`${source} must be a positive integer (got '${raw}')\n`);
133
+ process.exit(2);
134
+ }
135
+ return n;
136
+ }
137
+ /** The vitest argv fragment for the resolved worker cap (empty when uncapped). */
138
+ function maxWorkersArgs(maxWorkers) {
139
+ return maxWorkers === undefined ? [] : ['--maxWorkers', String(maxWorkers)];
109
140
  }
110
141
  const HELP_TEXT = `openwop-conformance — run the openwop conformance suite against a server
111
142
 
@@ -129,6 +160,10 @@ Certification (RFC 0089):
129
160
  §C) records per-requirement DISPOSITIONS instead of pass/fail/skip
130
161
  file lists, so "we could not check" stops being indistinguishable
131
162
  from "checked and it holds". See the note it prints.
163
+ --max-workers <n> Cap concurrently running scenario files (vitest --maxWorkers).
164
+ Default: one worker per CPU. Use a small number against a
165
+ rate-limited production origin so 429s don't read as failures.
166
+ (env: OPENWOP_MAX_WORKERS)
132
167
  --certify <out.json> Generate a machine-readable conformance certification
133
168
  bundle: fetch /.well-known/openwop (captured verbatim +
134
169
  SHA-256), derive claimedProfiles from it, run the suite
@@ -271,6 +306,7 @@ async function runCertify(args, baseUrl, apiKey) {
271
306
  resolvePath(conformanceRoot, 'vitest.config.ts'),
272
307
  '--reporter=json',
273
308
  `--outputFile=${reportFile}`,
309
+ ...maxWorkersArgs(args.maxWorkers),
274
310
  ];
275
311
  const runResult = spawnSync('npx', vitestArgs, { cwd: conformanceRoot, env, stdio: 'inherit' });
276
312
  if (runResult.error) {
@@ -528,6 +564,7 @@ async function main() {
528
564
  if (args.filter) {
529
565
  vitestArgs.push('--testNamePattern', args.filter);
530
566
  }
567
+ vitestArgs.push(...maxWorkersArgs(args.maxWorkers));
531
568
  const result = spawnSync('npx', ['vitest', ...vitestArgs], {
532
569
  cwd: conformanceRoot,
533
570
  env,
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.136.2",
3
+ "version": "1.136.4",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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.2",
4
- "corpusCommit": "4123dfae532f80231763fc43f8d3df3022c58c3d"
3
+ "suiteVersion": "1.136.4",
4
+ "corpusCommit": "abb3c77ccf7f952fbaa66b06463964fa9dd3ad36"
5
5
  }
package/src/cli.ts CHANGED
@@ -12,6 +12,7 @@
12
12
  * openwop-conformance --filter discovery # category filter
13
13
  * openwop-conformance --base-url ... --api-key ... --filter "interrupt|cancellation"
14
14
  * openwop-conformance --base-url ... --api-key ... --certify out.json # RFC 0089 bundle
15
+ * openwop-conformance --base-url ... --api-key ... --max-workers 4 # cap parallel scenario files
15
16
  *
16
17
  * Environment variables override flags (per the conformance harness's
17
18
  * existing convention):
@@ -55,6 +56,13 @@ interface ParsedArgs {
55
56
  /** RFC 0089 — emit a conformance certification bundle to this path. */
56
57
  readonly certify: string | undefined;
57
58
  readonly bundleVersion: '1' | '2';
59
+ /**
60
+ * S43 (2026-08-18) — cap on concurrently running scenario FILES, forwarded to
61
+ * vitest `--maxWorkers`. Unset = vitest's default (one worker per CPU), which
62
+ * hammers a rate-limited production origin with ~460 files at once and turns
63
+ * `429`s into spurious reds. Env: `OPENWOP_MAX_WORKERS`.
64
+ */
65
+ readonly maxWorkers: number | undefined;
58
66
  }
59
67
 
60
68
  function parseArgs(argv: readonly string[]): ParsedArgs {
@@ -67,6 +75,7 @@ function parseArgs(argv: readonly string[]): ParsedArgs {
67
75
  let implVersion: string | undefined;
68
76
  let certify: string | undefined;
69
77
  let bundleVersion: '1' | '2' = '1';
78
+ let maxWorkers: number | undefined = parseMaxWorkers(process.env.OPENWOP_MAX_WORKERS, 'OPENWOP_MAX_WORKERS');
70
79
 
71
80
  for (let i = 0; i < argv.length; i++) {
72
81
  const arg = argv[i] ?? '';
@@ -121,6 +130,9 @@ function parseArgs(argv: readonly string[]): ParsedArgs {
121
130
  case '--certify':
122
131
  certify = nextValue();
123
132
  break;
133
+ case '--max-workers':
134
+ maxWorkers = parseMaxWorkers(nextValue(), '--max-workers');
135
+ break;
124
136
  default:
125
137
  if (arg.startsWith('-')) {
126
138
  // Unknown flag — pass through to vitest by ignoring here.
@@ -128,7 +140,34 @@ function parseArgs(argv: readonly string[]): ParsedArgs {
128
140
  }
129
141
  }
130
142
 
131
- return { baseUrl, apiKey, offline, filter, help, impl, implVersion, certify, bundleVersion };
143
+ return {
144
+ baseUrl,
145
+ apiKey,
146
+ offline,
147
+ filter,
148
+ help,
149
+ impl,
150
+ implVersion,
151
+ certify,
152
+ bundleVersion,
153
+ maxWorkers,
154
+ };
155
+ }
156
+
157
+ /** Parse a `--max-workers` / `OPENWOP_MAX_WORKERS` value: a positive integer, else exit 2. */
158
+ function parseMaxWorkers(raw: string | undefined, source: string): number | undefined {
159
+ if (raw === undefined || raw === '') return undefined;
160
+ const n = Number(raw);
161
+ if (!Number.isInteger(n) || n < 1) {
162
+ process.stderr.write(`${source} must be a positive integer (got '${raw}')\n`);
163
+ process.exit(2);
164
+ }
165
+ return n;
166
+ }
167
+
168
+ /** The vitest argv fragment for the resolved worker cap (empty when uncapped). */
169
+ function maxWorkersArgs(maxWorkers: number | undefined): string[] {
170
+ return maxWorkers === undefined ? [] : ['--maxWorkers', String(maxWorkers)];
132
171
  }
133
172
 
134
173
  const HELP_TEXT = `openwop-conformance — run the openwop conformance suite against a server
@@ -153,6 +192,10 @@ Certification (RFC 0089):
153
192
  §C) records per-requirement DISPOSITIONS instead of pass/fail/skip
154
193
  file lists, so "we could not check" stops being indistinguishable
155
194
  from "checked and it holds". See the note it prints.
195
+ --max-workers <n> Cap concurrently running scenario files (vitest --maxWorkers).
196
+ Default: one worker per CPU. Use a small number against a
197
+ rate-limited production origin so 429s don't read as failures.
198
+ (env: OPENWOP_MAX_WORKERS)
156
199
  --certify <out.json> Generate a machine-readable conformance certification
157
200
  bundle: fetch /.well-known/openwop (captured verbatim +
158
201
  SHA-256), derive claimedProfiles from it, run the suite
@@ -306,6 +349,7 @@ async function runCertify(args: ParsedArgs, baseUrl: string, apiKey: string): Pr
306
349
  resolvePath(conformanceRoot, 'vitest.config.ts'),
307
350
  '--reporter=json',
308
351
  `--outputFile=${reportFile}`,
352
+ ...maxWorkersArgs(args.maxWorkers),
309
353
  ];
310
354
  const runResult = spawnSync('npx', vitestArgs, { cwd: conformanceRoot, env, stdio: 'inherit' });
311
355
  if (runResult.error) {
@@ -605,6 +649,7 @@ async function main(): Promise<never> {
605
649
  if (args.filter) {
606
650
  vitestArgs.push('--testNamePattern', args.filter);
607
651
  }
652
+ vitestArgs.push(...maxWorkersArgs(args.maxWorkers));
608
653
 
609
654
  const result = spawnSync('npx', ['vitest', ...vitestArgs], {
610
655
  cwd: conformanceRoot,
@@ -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(tenantId: string): Promise<string | null> {
27
+ export async function seedRun(_label: string): Promise<string | null> {
28
28
  if (!isFixtureAdvertised(SEED_FIXTURE)) return null;
29
- const r = await driver.post('/v1/runs', { workflowId: SEED_FIXTURE, tenantId, inputs: {} });
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(tenantId: string): Promise<string | null> {
27
+ export async function seedRun(_label: string): Promise<string | null> {
28
28
  if (!isFixtureAdvertised(SEED_FIXTURE)) return null;
29
- const r = await driver.post('/v1/runs', { workflowId: SEED_FIXTURE, tenantId, inputs: {} });
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
  }
@@ -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 { SCHEMAS_DIR } from '../lib/paths.js';
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
- const CHAIN_DOC = join(SCHEMAS_DIR, '..', 'spec', 'v1', 'workflow-chain-packs.md');
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
- const CHAIN_DOC = join(SCHEMAS_DIR, '..', 'spec', 'v1', 'workflow-chain-packs.md');
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),
@@ -30,6 +30,7 @@
30
30
 
31
31
  import { describe, it, expect } from 'vitest';
32
32
  import { readFileSync } from 'node:fs';
33
+ import { randomUUID } from 'node:crypto';
33
34
  import { join } from 'node:path';
34
35
  import { driver } from '../lib/driver.js';
35
36
  import { behaviorGate } from '../lib/behavior-gate.js';
@@ -39,6 +40,17 @@ import { readErrorCode, readRetriable } from '../lib/error-envelope.js';
39
40
 
40
41
  const GATE = 'openwop-self-hosted-runner';
41
42
 
43
+ // S40 (2026-08-18): every registration / dispatch id carries a per-run nonce. The
44
+ // seam drives the host's REAL runner registry + `{runId, stepId}` result store
45
+ // (host-sample-test-seams.md §19), so fixed ids (`runner_a_1`, `run_idem`/`step_1`)
46
+ // collide with the previous certification run on any host whose store outlives
47
+ // the process: the second run's FIRST dispatch is already `deduped:true` and the
48
+ // at-most-once leg no longer proves anything, and re-registering a fixed runnerId
49
+ // may be refused. Fresh ids per run keep both legs non-vacuous on a durable host.
50
+ const NONCE = randomUUID().slice(0, 8);
51
+ const SUBJECT_A = `subject_A_${NONCE}`;
52
+ const SUBJECT_B = `subject_B_${NONCE}`;
53
+
42
54
  interface JsonSchema {
43
55
  properties?: Record<string, JsonSchema>;
44
56
  required?: string[];
@@ -155,15 +167,15 @@ describe('self-hosted-runner: behavioral (seam-gated, soft-skip 404)', () => {
155
167
  // MUST NOT fall back to B's runner (subject-first isolation); with no runner
156
168
  // for A the dispatch MUST fail with the retriable `runner_unavailable`.
157
169
  const reg = await driver.post(REGISTER, {
158
- runnerId: 'runner_b_1',
159
- subject: 'subject_B',
170
+ runnerId: `runner_b_${NONCE}`,
171
+ subject: SUBJECT_B,
160
172
  capabilities: { providers: ['anthropic'] },
161
173
  });
162
174
  if (reg.status === 404) return; // seam unwired — soft-skip
163
175
 
164
176
  const res = await driver.post(DISPATCH, {
165
- subject: 'subject_A',
166
- runId: 'run_iso',
177
+ subject: SUBJECT_A,
178
+ runId: `run_iso_${NONCE}`,
167
179
  stepId: 'step_0',
168
180
  seq: 0,
169
181
  kind: 'model',
@@ -189,15 +201,15 @@ describe('self-hosted-runner: behavioral (seam-gated, soft-skip 404)', () => {
189
201
 
190
202
  it('a redelivered {runId, stepId} dispatch is dropped, not re-executed (at-most-once)', async () => {
191
203
  const reg = await driver.post(REGISTER, {
192
- runnerId: 'runner_a_1',
193
- subject: 'subject_A',
204
+ runnerId: `runner_a_${NONCE}`,
205
+ subject: SUBJECT_A,
194
206
  capabilities: { providers: ['anthropic'] },
195
207
  });
196
208
  if (reg.status === 404) return;
197
209
 
198
210
  const frame = {
199
- subject: 'subject_A',
200
- runId: 'run_idem',
211
+ subject: SUBJECT_A,
212
+ runId: `run_idem_${NONCE}`,
201
213
  stepId: 'step_1',
202
214
  seq: 0,
203
215
  kind: 'model',
@@ -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
- const CHAIN_DOC = join(SCHEMAS_DIR, '..', 'spec', 'v1', 'workflow-chain-packs.md');
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
- const CHAIN_DOC = join(SCHEMAS_DIR, '..', 'spec', 'v1', 'workflow-chain-packs.md');
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
- const CHAIN_DOC = join(SCHEMAS_DIR, '..', 'spec', 'v1', 'workflow-chain-packs.md');
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(join(SCHEMAS_DIR, '..', 'RFCS', '0136-workflow-variable-format.md'), 'utf8');
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'),