@openwop/openwop-conformance 2.43.0 → 2.43.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.
@@ -15,8 +15,10 @@
15
15
  * packs at all (`pack_kind_invalid`) records `skipped` for that leg.
16
16
  *
17
17
  * Installs through the seams-profile publish seam
18
- * (`/conformance/seams/packs-test/…`, RFC 0168 §C.2). Gated on `packs` + the
19
- * seams profile.
18
+ * (`/conformance/seams/packs-test/…`, RFC 0168 §C.2). Gated on the seams
19
+ * profile; not on `packs` since suite 2.43.1 (#1706) — the hatch binds at install
20
+ * on every path. A seam 404 records `blocked` when `packs` is advertised and
21
+ * `inapplicable` when it is not.
20
22
  *
21
23
  * @see RFCS/0177-v2-registry-packs-and-extension-tail.md §C.2
22
24
  * @see spec/v2/core/packs.md §"The manifest schema family"
@@ -30,7 +32,7 @@ import { readErrorCode } from '../lib/error-envelope.js';
30
32
  import { req } from '../lib/requirement-ids.js';
31
33
  import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
32
34
  import { seamsProfileAdvertised, targetMajor } from '../lib/seams.js';
33
- import { v2Discovery, gateFamily } from '../lib/v2.js';
35
+ import { v2Discovery, familyAdvertised } from '../lib/v2.js';
34
36
 
35
37
  const SECTION = 'packs.md §"The manifest schema family" (RFC 0177 §C.2)';
36
38
 
@@ -67,6 +69,17 @@ async function publish(manifest: Record<string, unknown>, files: Record<string,
67
69
  headers: { 'Content-Type': 'application/octet-stream' },
68
70
  });
69
71
  }
72
+ /**
73
+ * The publish seam answered 404. Whether that is a defect depends on `packs`
74
+ * (#1706): a host advertising `packs` claims an install path and must mount it
75
+ * (`blocked`); a host that omits `packs` may have no install path the suite can
76
+ * drive (`inapplicable`). §A.1 binds at install on every path, not on `packs`.
77
+ */
78
+ async function seamMissing(): Promise<undefined> {
79
+ if (await familyAdvertised('packs')) return softSkip('blocked', 'packs-test publish seam answered 404 — packs and the seams profile are advertised but the seam is not mounted');
80
+ return softSkip('inapplicable', 'packs-test publish seam answered 404 and packs is not advertised — no install path the suite can drive');
81
+ }
82
+
70
83
  async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | null> {
71
84
  if (targetMajor() !== 2) return { kind: 'inapplicable', reason: 'suite 2.0.0 v2 scenario: OPENWOP_TARGET_MAJOR is not 2' };
72
85
  let doc: Record<string, unknown> | null;
@@ -76,7 +89,6 @@ async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | nul
76
89
  doc = null;
77
90
  }
78
91
  if (!doc) return { kind: 'blocked', reason: 'discovery unreachable — /.well-known/openwop (OpenWOP-Version: 2.0) did not answer 200 JSON' };
79
- if (!(await gateFamily('packs'))) return { kind: 'inapplicable', reason: 'v2 discovery does not advertise the packs family (RFC 0169 §A.2)' };
80
92
  if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'host does not advertise conformance.seamsProfile: openwop-conformance-seams-v2 — the packs-test publish seam is the only install path the suite can drive (RFC 0168 §C.1)' };
81
93
  return null;
82
94
  }
@@ -93,10 +105,14 @@ describe('v2-manifest-hatch-carried (RFC 0177 §C.2)', () => {
93
105
  engines: { openwop: '>=2.0.0 <3.0.0' },
94
106
  runtime: { language: 'javascript', entry: 'index.mjs', format: 'esm' },
95
107
  nodes: [{ typeId: `${name}.echo`, version: '1.0.0', category: 'data', role: 'pure' }],
96
- agents: [{ agentId: `${name}.helper`, persona: 'Hatch Helper', modelClass: 'general', 'x-vendor-note': 'a hatch property the host MUST ignore' }],
108
+ // systemPrompt: agent-manifest requires exactly one of systemPrompt /
109
+ // systemPromptRef (oneOf). Without it the fixture was schema-invalid for a
110
+ // reason unrelated to the hatch, and a conforming host had to refuse it
111
+ // (2.43.1; reported by MyndHyve).
112
+ agents: [{ agentId: `${name}.helper`, persona: 'Hatch Helper', modelClass: 'general', systemPrompt: 'You are a conformance fixture.', 'x-vendor-note': 'a hatch property the host MUST ignore' }],
97
113
  };
98
114
  const res = await publish(manifest, { 'index.mjs': ENTRY });
99
- if (res.status === 404) return softSkip('blocked', 'packs-test publish seam answered 404 — seams profile advertised but the seam is not mounted');
115
+ if (res.status === 404) return seamMissing();
100
116
  expect([200, 201].includes(res.status), req('openwop.requirement.0177.manifest-hatch-carried.agents-x-field', SECTION, `agent-manifest admits ^(openwop-|x-|vendor\\.) — a pack whose agents[0] carries x-vendor-note MUST install (got ${res.status} ${readErrorCode(res.json) ?? ''})`)).toBe(true);
101
117
  });
102
118
 
@@ -112,7 +128,7 @@ describe('v2-manifest-hatch-carried (RFC 0177 §C.2)', () => {
112
128
  prompts: [{ templateId: `${name}.system`, version: '1.0.0', kind: 'system', text: 'You are a conformance fixture.', 'x-vendor-note': 'a hatch property the host MUST ignore' }],
113
129
  };
114
130
  const res = await publish(manifest, {});
115
- if (res.status === 404) return softSkip('blocked', 'packs-test publish seam answered 404 — seams profile advertised but the seam is not mounted');
131
+ if (res.status === 404) return seamMissing();
116
132
  const code = readErrorCode(res.json);
117
133
  if (res.status >= 400 && code === 'pack_kind_invalid') return softSkip('skipped', 'host does not install kind: prompt packs (pack_kind_invalid) — the prompt-template hatch cannot be observed on this host');
118
134
  expect([200, 201].includes(res.status), req('openwop.requirement.0177.manifest-hatch-carried.prompt-template-x-field', SECTION, `prompt-template admits ^(openwop-|x-|vendor\\.) — a prompt pack whose prompts[0] carries x-vendor-note MUST install (got ${res.status} ${code ?? ''})`)).toBe(true);
@@ -1,5 +1,10 @@
1
1
  /**
2
- * RFC 0173 §B — `pack-isolation` (suite 2.0.0, target major 2; gated on `packs` + `sandbox`).
2
+ * RFC 0173 §B — `pack-isolation` (suite 2.0.0, target major 2; gated on `sandbox`).
3
+ *
4
+ * Gated on `sandbox` alone since suite 2.43.1 (#1706). It was gated on `packs`
5
+ * + `sandbox`, but `packs` advertises registry resolution (packs.md §The
6
+ * `packs` capability), which a host executing only local packs correctly omits.
7
+ * `sandbox` is the advertisement §B requires of any host executing packs.
3
8
  *
4
9
  * Isolation binds with pack EXECUTION: a host that executes third-party packs
5
10
  * MUST enforce the eight `node-pack-sandbox-*` invariants and MUST advertise
@@ -41,11 +46,10 @@ async function discovery(): Promise<Record<string, unknown> | null> {
41
46
  try { return await v2Discovery(); } catch { return null; }
42
47
  }
43
48
 
44
- /** Gate for every leg: packs + sandbox advertised. Returns the sandbox facet or null (reason recorded). */
49
+ /** Gate for every leg: sandbox advertised. Returns the sandbox facet or null (reason recorded). */
45
50
  async function gated(): Promise<Record<string, unknown> | null> {
46
51
  const doc = await discovery();
47
52
  if (!doc) { softSkip('blocked', 'discovery unreachable'); return null; }
48
- if (!(await gateFamily('packs'))) { softSkip('inapplicable', 'packs family not advertised — no pack execution, no isolation obligation (gate recorded under openwop.family.packs)'); return null; }
49
53
  const sandbox = await gateFamily('sandbox');
50
54
  if (!sandbox) { softSkip('inapplicable', 'sandbox family not advertised (gate recorded under openwop.family.sandbox)'); return null; }
51
55
  return sandbox;
@@ -56,7 +60,7 @@ async function invoke(typeId: string, extra: Record<string, unknown> = {}): Prom
56
60
  const doc = await discovery();
57
61
  if (!doc || !seamsProfileAdvertised(doc)) { softSkip('inapplicable', `the ${typeId} leg is seam-driven — seams profile (conformance.seamsProfile = openwop-conformance-seams-v2) not advertised`); return null; }
58
62
  const res = await driver.post(INVOKE, { typeId, ...extra });
59
- if (res.status === 404 || res.status === 403 || res.status === 405) { seamAbsent(`host advertises packs + sandbox but ${INVOKE} answered ${res.status} — the ${typeId} leg is unobservable (host-sample-test-seams.md §8)`); return null; }
63
+ if (res.status === 404 || res.status === 403 || res.status === 405) { seamAbsent(`host advertises sandbox but ${INVOKE} answered ${res.status} — the ${typeId} leg is unobservable (host-sample-test-seams.md §8)`); return null; }
60
64
  // The same id is ALSO asserted by its own `it` above, and that is the copy the
61
65
  // registry can see: `generate-requirement-registry.mjs` harvests `req(…)` only
62
66
  // within an `it`, so this helper-level call alone left the id out of
@@ -74,7 +78,7 @@ function expectEscape(id: string, r: InvokeResult, escapeKind: string, invariant
74
78
  expect(r.error?.details?.escapeKind, req(id, 'host-sample-test-seams.md §8 SandboxError', `escapeKind MUST be ${escapeKind}`)).toBe(escapeKind);
75
79
  }
76
80
 
77
- describe('RFC 0173 §B — pack-isolation (gated on packs + sandbox)', () => {
81
+ describe('RFC 0173 §B — pack-isolation (gated on sandbox)', () => {
78
82
  it('sandbox.isolationModel names a real mechanism, never node:vm', async () => {
79
83
  const sandbox = await gated();
80
84
  if (!sandbox) return softSkip('inapplicable', 'gate not met (reason recorded above)');
@@ -94,7 +98,7 @@ describe('RFC 0173 §B — pack-isolation (gated on packs + sandbox)', () => {
94
98
  const doc = await discovery();
95
99
  if (!doc || !seamsProfileAdvertised(doc)) return softSkip('inapplicable', 'seam-driven — seams profile (conformance.seamsProfile = openwop-conformance-seams-v2) not advertised');
96
100
  const res = await driver.post(INVOKE, { typeId: 'misbehave.fs-escape-read' });
97
- if (res.status === 404 || res.status === 403 || res.status === 405) return seamAbsent(`host advertises packs + sandbox but ${INVOKE} answered ${res.status}`);
101
+ if (res.status === 404 || res.status === 403 || res.status === 405) return seamAbsent(`host advertises sandbox but ${INVOKE} answered ${res.status}`);
98
102
  expect(
99
103
  res.status,
100
104
  req('openwop.requirement.0173.pack-isolation.seam', 'host-sample-test-seams.md §8', `${INVOKE} MUST answer 200 { result } | 200 { error } — the eight behavioural legs are unobservable otherwise`),
@@ -17,8 +17,10 @@
17
17
  * failure; any other refusal fails.
18
18
  *
19
19
  * Both legs install through the seams-profile publish seam
20
- * (`/conformance/seams/packs-test/…`, RFC 0168 §C.2). Gated on `packs` + the
21
- * seams profile.
20
+ * (`/conformance/seams/packs-test/…`, RFC 0168 §C.2). Gated on the seams
21
+ * profile; not on `packs` since suite 2.43.1 (#1706) — the rule binds at install
22
+ * on every path. A seam 404 records `blocked` when `packs` is advertised and
23
+ * `inapplicable` when it is not.
22
24
  *
23
25
  * @see RFCS/0177-v2-registry-packs-and-extension-tail.md §B.1, §B.2
24
26
  * @see spec/v2/core/packs.md §"Peer-dependency identifiers", §"The alias table"
@@ -35,7 +37,7 @@ import { SCHEMAS_DIR } from '../lib/paths.js';
35
37
  import { req } from '../lib/requirement-ids.js';
36
38
  import { softSkip, type SoftSkipKind } from '../lib/soft-skip.js';
37
39
  import { seamsProfileAdvertised, targetMajor } from '../lib/seams.js';
38
- import { v2Discovery, gateFamily, familyAdvertised } from '../lib/v2.js';
40
+ import { v2Discovery, familyAdvertised } from '../lib/v2.js';
39
41
 
40
42
  const SECTION = 'packs.md §"Peer-dependency identifiers" (RFC 0177 §B.1)';
41
43
  const ALIASES = join(SCHEMAS_DIR, '..', 'spec', 'v2', 'peer-dependency-aliases.json');
@@ -86,6 +88,17 @@ async function publish(manifest: Record<string, unknown>) {
86
88
  headers: { 'Content-Type': 'application/octet-stream' },
87
89
  });
88
90
  }
91
+ /**
92
+ * The publish seam answered 404. Whether that is a defect depends on `packs`
93
+ * (#1706): a host advertising `packs` claims an install path and must mount it
94
+ * (`blocked`); a host that omits `packs` may have no install path the suite can
95
+ * drive (`inapplicable`). §A.1 binds at install on every path, not on `packs`.
96
+ */
97
+ async function seamMissing(): Promise<undefined> {
98
+ if (await familyAdvertised('packs')) return softSkip('blocked', 'packs-test publish seam answered 404 — packs and the seams profile are advertised but the seam is not mounted');
99
+ return softSkip('inapplicable', 'packs-test publish seam answered 404 and packs is not advertised — no install path the suite can drive');
100
+ }
101
+
89
102
  async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | null> {
90
103
  if (targetMajor() !== 2) return { kind: 'inapplicable', reason: 'suite 2.0.0 v2 scenario: OPENWOP_TARGET_MAJOR is not 2' };
91
104
  let doc: Record<string, unknown> | null;
@@ -95,7 +108,6 @@ async function preflight(): Promise<{ kind: SoftSkipKind; reason: string } | nul
95
108
  doc = null;
96
109
  }
97
110
  if (!doc) return { kind: 'blocked', reason: 'discovery unreachable — /.well-known/openwop (OpenWOP-Version: 2.0) did not answer 200 JSON' };
98
- if (!(await gateFamily('packs'))) return { kind: 'inapplicable', reason: 'v2 discovery does not advertise the packs family (RFC 0169 §A.2)' };
99
111
  if (!seamsProfileAdvertised(doc)) return { kind: 'inapplicable', reason: 'host does not advertise conformance.seamsProfile: openwop-conformance-seams-v2 — the packs-test publish seam is the only install path the suite can drive (RFC 0168 §C.1)' };
100
112
  return null;
101
113
  }
@@ -105,7 +117,7 @@ describe('v2-peer-dependency-declared (RFC 0177 §B.1/§B.2)', () => {
105
117
  const skip = await preflight();
106
118
  if (skip) return softSkip(skip.kind, skip.reason);
107
119
  const res = await publish(nodePack(freshName('peer-undefined'), { 'host.nonexistent': 'required' }));
108
- if (res.status === 404) return softSkip('blocked', 'packs-test publish seam answered 404 — seams profile advertised but the seam is not mounted');
120
+ if (res.status === 404) return seamMissing();
109
121
  expect(res.status, req('openwop.requirement.0177.peer-dependency-declared.undefined-key', SECTION, 'a key absent from spec/v2/declaration.json MUST be refused with 400 pack_peer_dependency_undefined')).toBe(400);
110
122
  expect(readErrorCode(res.json), req('openwop.requirement.0177.peer-dependency-declared.undefined-key', SECTION, 'the refusal code MUST be pack_peer_dependency_undefined')).toBe('pack_peer_dependency_undefined');
111
123
  });
@@ -124,7 +136,7 @@ describe('v2-peer-dependency-declared (RFC 0177 §B.1/§B.2)', () => {
124
136
  }
125
137
  if (!chosen) return softSkip('inapplicable', 'no facet-less alias row names a family this host advertises — the overlap alias cannot be exercised');
126
138
  const res = await publish(nodePack(freshName('peer-alias'), { [chosen.alias]: 'required' }));
127
- if (res.status === 404) return softSkip('blocked', 'packs-test publish seam answered 404 — seams profile advertised but the seam is not mounted');
139
+ if (res.status === 404) return seamMissing();
128
140
  const code = readErrorCode(res.json);
129
141
  if (res.status === 400 && code === 'pack_peer_dependency_undefined') return softSkip('skipped', `host does not resolve the overlap alias ${chosen.alias} (RFC 0177 §B.2 is a MAY during the overlap; MUST NOT after v1 end-of-support)`);
130
142
  expect([200, 201].includes(res.status), req('openwop.requirement.0177.peer-dependency-declared.alias-overlap', 'packs.md §"The alias table" (RFC 0177 §B.2)', `alias ${chosen.alias} → ${chosen.family} (advertised) MUST install through the overlap or be refused only with pack_peer_dependency_undefined (got ${res.status} ${code ?? ''})`)).toBe(true);
@@ -10,15 +10,20 @@
10
10
  * checked before lookup (`403 id_tenant_mismatch`), refuses a subscription that
11
11
  * did not opt in (`400 validation_error`), and returns no secret.
12
12
  *
13
- * The post-overlap leg waits for `previousSecretExpiresAt` only when
14
- * `overlapSeconds` fits inside the suite's retry-wait cap
15
- * (`OPENWOP_WEBHOOK_RETRY_WAIT_MS`, operator-raisable). Past the cap it is not
16
- * observed, and the row says so with a `partial-witness:` detail — which the
17
- * acceptance predicate refuses to cite as `executed-pass` (RFC 0201 §Falsifiability).
13
+ * The post-overlap leg waits for `previousSecretExpiresAt` when `overlapSeconds`
14
+ * fits inside the suite's retry-wait cap (`OPENWOP_WEBHOOK_RETRY_WAIT_MS`,
15
+ * operator-raisable). Past the cap (suite 2.43.1), a seams-profile host that
16
+ * serves the OPTIONAL `shortenRotationOverlap` seam (host-sample-test-seams.md
17
+ * §29) has this one subscription's overlap cut to a few seconds, and the leg
18
+ * waits for that; a served seam that breaks its contract records `blocked`.
19
+ * A host without the seam is not observed, and the row says so with a
20
+ * `partial-witness:` detail — which the acceptance predicate refuses to cite as
21
+ * `executed-pass` (RFC 0201 §Falsifiability).
18
22
  *
19
23
  * How it FAILS: signing only with the new secret (one entry); moving
20
24
  * `OpenWOP-Signature` to the new secret at rotation; a foreign-tenant id that
21
- * reaches the lookup (`404` instead of `403`); a response that echoes the secret.
25
+ * reaches the lookup (`404` instead of `403`); a response that echoes the secret;
26
+ * the previous secret still signing after the (shortened) overlap.
22
27
  *
23
28
  * @see spec/v2/core/webhooks.md §Standard Webhooks
24
29
  * @see RFCS/0201-standard-webhooks-signature-scheme.md §E
@@ -32,6 +37,7 @@ import { readErrorCode } from '../lib/error-envelope.js';
32
37
  import { softSkip } from '../lib/soft-skip.js';
33
38
  import { projectBoundId } from '../lib/bound-id.js';
34
39
  import { retryWaitCapMs } from '../lib/webhook-retry-window.js';
40
+ import { seamsProfileAdvertised } from '../lib/seams.js';
35
41
  import { hitHeader, mintWhsec, startModalReceiver, verifyStandardWebhooks, type ModalHit } from '../lib/webhook-receiver.js';
36
42
  import {
37
43
  STANDARD_WEBHOOKS_ALG,
@@ -39,12 +45,15 @@ import {
39
45
  driveRun,
40
46
  loopbackRefusal,
41
47
  registerSw,
48
+ shortenRotationOverlap,
42
49
  swGate,
43
50
  unregisterAllSw,
44
51
  waitFor,
45
52
  } from '../lib/standard-webhooks.js';
46
53
 
47
54
  const CAP_MS = retryWaitCapMs();
55
+ /** The overlap the §29 seam sets: long enough that the call returns inside it, short enough to wait out. */
56
+ const SEAM_OVERLAP_SECONDS = 3;
48
57
 
49
58
  let closeReceiver: (() => Promise<void>) | null = null;
50
59
  afterEach(async () => {
@@ -114,17 +123,25 @@ describe('RFC 0201 §E — secret rotation overlaps, then retires (gated on webh
114
123
  expect(underOld.matched, req(ID, 'RFC 0201 §E.20', 'one entry MUST verify under the previous secret')).toBe(1);
115
124
  expect(v1SignedBy(s1, d), req(ID, 'RFC 0201 §E.20', 'OpenWOP-Signature MUST stay on the previous secret during the overlap')).toBe(true);
116
125
 
117
- // After the overlap: only when it fits the operator's wait cap.
118
- const expiresAt = Date.parse(body.previousSecretExpiresAt ?? '');
126
+ // After the overlap: when it fits the operator's wait cap, or when the §29 seam shortens it.
127
+ let expiresAt = Date.parse(body.previousSecretExpiresAt ?? '');
119
128
  if (expiresAt - Date.now() > CAP_MS) {
129
+ const unobserved = (why: string): string => `the post-overlap leg is not observed: the advertised webhooks.secretRotation.overlapSeconds is ${overlapSeconds}s (expiry ${new Date(expiresAt).toISOString()}) and the suite wait cap is ${CAP_MS}ms, and ${why}; raise OPENWOP_WEBHOOK_RETRY_WAIT_MS past the overlap, or serve the seam, to witness it`;
120
130
  // partial-witness-ok: the tenant check, the rotate response and the dual signature during
121
131
  // the overlap were asserted above. "The previous secret MUST NOT sign after
122
132
  // previousSecretExpiresAt" cannot be observed inside a bounded run when the host's
123
133
  // advertised webhooks.secretRotation.overlapSeconds outlasts the suite's wait cap, and
124
134
  // blocking every long-overlap host would deny certification to conforming hosts
125
- // (steward ruling, 2026-09-27). The row detail names both numbers. Raise
126
- // OPENWOP_WEBHOOK_RETRY_WAIT_MS past the overlap to witness it.
127
- return softSkip('inapplicable', `the post-overlap leg is not observed: the advertised webhooks.secretRotation.overlapSeconds is ${overlapSeconds}s (expiry ${new Date(expiresAt).toISOString()}) and the suite wait cap is ${CAP_MS}ms; raise OPENWOP_WEBHOOK_RETRY_WAIT_MS past the overlap to witness it`);
135
+ // (steward ruling, 2026-09-27). The §29 seam closes it, but only for a host that
136
+ // claims the seams profile; this one did not. The row detail names both numbers.
137
+ if (!seamsProfileAdvertised(g.doc)) return softSkip('inapplicable', unobserved('the seams profile is not advertised, so the optional shortenRotationOverlap seam (host-sample-test-seams.md §29) is not offered'));
138
+ const cut = await shortenRotationOverlap(webhookId, SEAM_OVERLAP_SECONDS, foreign);
139
+ // partial-witness-ok: as above — the dual-signing half was asserted. The §29 seam is
140
+ // OPTIONAL and newer than this leg, so a host that does not serve it keeps the partial
141
+ // witness it held before the seam existed, never `blocked`.
142
+ if (!cut.ok && cut.unserved) return softSkip('inapplicable', unobserved(cut.reason));
143
+ if (!cut.ok) return softSkip('blocked', cut.reason);
144
+ expiresAt = cut.expiresAt;
128
145
  }
129
146
  await new Promise((r) => setTimeout(r, Math.max(0, expiresAt - Date.now()) + 1_500));
130
147
  const after = await oneDelivery(rx, webhookId);