@openwop/openwop-conformance 1.130.0 → 1.132.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.130.0",
3
+ "version": "1.132.0",
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.130.0",
4
- "corpusCommit": "6c603a19a1985f2ae05ef77504e2c189c9ea75bf"
3
+ "suiteVersion": "1.132.0",
4
+ "corpusCommit": "1f702f91558437558816d0e6af251926942d123f"
5
5
  }
@@ -31,16 +31,29 @@
31
31
  * (soft-skips without the advert, HARD-FAILS under `OPENWOP_REQUIRE_BEHAVIOR=true`)
32
32
  * and records its RFC 0148 §A disposition. The three sub-features are
33
33
  * INDEPENDENTLY optional: a host that wires the base seams but not this
34
- * extension is `blocked` here (named as such via `seamAbsent`) and keeps its
34
+ * extension is `blocked` here (named as such via `softSkip`) and keeps its
35
35
  * `compensation-behavior` witness — the base and the extension are different
36
36
  * claims and are recorded separately.
37
+ *
38
+ * Mode does NOT change that (S24, corrected 2026-08-16 after the first host to
39
+ * wire §21 read the contract back to us): the recovery extension has NO advert
40
+ * flag — `capabilities.compensation` is closed over `supported` /
41
+ * `profileVersion` / `orderingModels` / `manualIntervention` — so a host cannot
42
+ * claim or disclaim it, and `seamAbsent` (whose contract is "an ADVERTISED
43
+ * capability whose observation seam is absent", strict-failing) is the wrong
44
+ * tool for it. An absent extension is `softSkip('blocked', …)` in both modes,
45
+ * exactly as §21 says; the ledger still refuses to certify the three §G
46
+ * invariants, so nothing weakens. The BASE seams (`unwind` / `replay`) are a
47
+ * different matter: `compensation.supported` promises them, and a 404 there
48
+ * stays `seamAbsent` (strict-fails) — that is `compensation-behavior`'s wall,
49
+ * not this file's.
37
50
  */
38
51
 
39
52
  import { describe, it, expect } from 'vitest';
40
53
  import { driver } from '../lib/driver.js';
41
54
  import { behaviorGate } from '../lib/behavior-gate.js';
42
55
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
43
- import { seamAbsent } from '../lib/soft-skip.js';
56
+ import { seamAbsent, softSkip } from '../lib/soft-skip.js';
44
57
  import { readErrorCode, readRetriable } from '../lib/error-envelope.js';
45
58
 
46
59
  const PROFILE = 'openwop-compensation';
@@ -85,9 +98,14 @@ async function post(path: string, body: Record<string, unknown>): Promise<{ stat
85
98
  return { status: r.status, json: r.json };
86
99
  }
87
100
 
88
- /** The recovery extension is optional; a base seam that ignores the new fields is `blocked` here, not failed. */
101
+ /**
102
+ * The recovery extension is optional and has no advert flag, so its absence is
103
+ * `blocked` in BOTH modes — `softSkip`, never `seamAbsent` (S24). The three §G
104
+ * invariants stay uncertified either way; strict mode is not entitled to fail
105
+ * a host for a sub-seam it was never asked to claim.
106
+ */
89
107
  function extensionAbsent(what: string): undefined {
90
- return seamAbsent(`the §21 recovery extension is not wired: ${what} (host-sample-test-seams.md §21 "Recovery extension") — retry-stability / operator authority / recorded-facts are unobservable`);
108
+ return softSkip('blocked', `the §21 recovery extension is not wired: ${what} (host-sample-test-seams.md §21 "Recovery extension") — retry-stability / operator authority / recorded-facts are unobservable`);
91
109
  }
92
110
 
93
111
  describe('RFC 0151 §C — inverse-action identity is retry-stable (capability-gated behavior)', () => {
@@ -55,6 +55,15 @@ describe('org-position-no-authority-escalation (RFC 0087 §B, behavioral)', () =
55
55
  const chart = await getOrgChart();
56
56
  if (chart === null) return seamAbsent('host advertises org-position authority but GET /v1/agents/org-chart is not served');
57
57
 
58
+ // An EMPTY chart has nothing that could carry authority: the invariant is
59
+ // unobservable, and RFC 0148 §A says unobservable is `blocked`, not a
60
+ // zero-assertion pass (which is what this leg recorded on such a host).
61
+ if ((chart.members ?? []).length === 0 && (chart.departments ?? []).length === 0) {
62
+ return seamAbsent('host advertises org-position authority but the org chart is EMPTY — no member or department to check; the invariant is unobservable until the host seeds one');
63
+ }
64
+ // The chart root is a wire object too.
65
+ expectNoAuthority(chart as unknown as Record<string, unknown>, 'the org-chart root');
66
+
58
67
  for (const m of chart.members ?? []) {
59
68
  expectNoAuthority(m as Record<string, unknown>, 'an org-chart member');
60
69
  }
@@ -22,6 +22,7 @@
22
22
 
23
23
  import { describe, it, expect } from 'vitest';
24
24
  import { driver } from '../lib/driver.js';
25
+ import { softSkip, seamAbsent } from '../lib/soft-skip.js';
25
26
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
26
27
 
27
28
  interface DiscoveryWorkspace {
@@ -49,7 +50,7 @@ interface WorkspaceFile {
49
50
 
50
51
  describe('workspace-behavior: §C CRUD round-trip (RFC 0059)', () => {
51
52
  it('PUT creates v1, GET returns it, list omits content, DELETE removes it', async () => {
52
- if (!(await workspaceCap())) return;
53
+ if (!(await workspaceCap())) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
53
54
  const path = 'behavior/CRUD.md';
54
55
 
55
56
  const created = await driver.put(`${FILES}/${encodeURIComponent(path)}`, { content: 'first body' });
@@ -78,7 +79,7 @@ describe('workspace-behavior: §C CRUD round-trip (RFC 0059)', () => {
78
79
 
79
80
  describe('workspace-behavior: §C optimistic concurrency (RFC 0059)', () => {
80
81
  it('stale If-Match → 409 workspace_conflict; matching If-Match bumps version', async () => {
81
- if (!(await workspaceCap())) return;
82
+ if (!(await workspaceCap())) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
82
83
  const path = 'behavior/ETAG.md';
83
84
 
84
85
  const v1 = await driver.put(`${FILES}/${encodeURIComponent(path)}`, { content: 'v1' });
@@ -101,7 +102,7 @@ describe('workspace-behavior: §C optimistic concurrency (RFC 0059)', () => {
101
102
  describe('workspace-behavior: §C size ceiling (RFC 0059)', () => {
102
103
  it('content beyond maxFileBytes returns workspace_too_large', async () => {
103
104
  const cap = await workspaceCap();
104
- if (cap === null) return;
105
+ if (cap === null) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
105
106
  const max = typeof cap.maxFileBytes === 'number' ? cap.maxFileBytes : 1_048_576;
106
107
  const tooBig = 'x'.repeat(max + 1);
107
108
  const res = await driver.put(`${FILES}/${encodeURIComponent('behavior/TOOBIG.md')}`, { content: tooBig });
@@ -112,14 +113,14 @@ describe('workspace-behavior: §C size ceiling (RFC 0059)', () => {
112
113
 
113
114
  describe('workspace-behavior: §D run-start snapshot (RFC 0059)', () => {
114
115
  it('a run started after a write exposes the workspace snapshot', async () => {
115
- if (!(await workspaceCap())) return;
116
+ if (!(await workspaceCap())) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
116
117
  const path = 'behavior/SNAPSHOT.md';
117
118
  await driver.put(`${FILES}/${encodeURIComponent(path)}`, { content: 'snapshot body' });
118
119
 
119
120
  const create = await driver.post('/v1/runs', { workflowId: 'conformance-noop' });
120
- if (create.status !== 201 && create.status !== 200) return; // host lacks the noop fixture skip
121
+ if (create.status !== 201 && create.status !== 200) return seamAbsent(`host advertises workspace but the noop fixture run could not be created (POST /v1/runs → ${create.status})`);
121
122
  const runId = (create.json as { runId?: string }).runId;
122
- if (runId === undefined) return;
123
+ if (runId === undefined) return seamAbsent('host advertises workspace but the created run carried no runId');
123
124
 
124
125
  const snap = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
125
126
  const ws = (snap.json as { workspace?: Array<{ path: string }> }).workspace;
@@ -23,6 +23,7 @@
23
23
 
24
24
  import { describe, it, expect } from 'vitest';
25
25
  import { driver } from '../lib/driver.js';
26
+ import { softSkip } from '../lib/soft-skip.js';
26
27
  import { capabilityFamily } from '../lib/discovery-capabilities.js';
27
28
 
28
29
  interface DiscoveryWorkspace {
@@ -48,7 +49,7 @@ const POSITIVE_INT_FIELDS = ['maxFileBytes', 'maxFiles', 'maxVersions'] as const
48
49
  describe('workspace-capability-shape: advertisement shape (RFC 0059 §A)', () => {
49
50
  it('capabilities.workspace is either absent or well-formed', async () => {
50
51
  const ws = await readWorkspace();
51
- if (ws === null) return; // host doesn't advertise workspace at all
52
+ if (ws === null) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
52
53
  expect(
53
54
  typeof ws.supported,
54
55
  driver.describe(
@@ -60,7 +61,7 @@ describe('workspace-capability-shape: advertisement shape (RFC 0059 §A)', () =>
60
61
 
61
62
  it('maxFileBytes / maxFiles / maxVersions are positive integers when present', async () => {
62
63
  const ws = await readWorkspace();
63
- if (ws === null) return;
64
+ if (ws === null) return softSkip('inapplicable', 'optional advertisement — `capabilities.workspace` not advertised by this host (RFC 0059 §A)');
64
65
  for (const field of POSITIVE_INT_FIELDS) {
65
66
  const v = ws[field];
66
67
  if (v === undefined) continue;