@openwop/openwop-conformance 1.155.0 → 1.157.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/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/coverage.md +1 -0
- package/dist/lib/spec-coherence.js +1 -0
- package/package.json +2 -1
- package/requirements.json +30 -6
- package/schemas/CORPUS-STAMP.json +2 -2
- package/src/lib/spec-coherence.ts +1 -0
- package/src/scenarios/owner-subject-echo.test.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# `@openwop/openwop-conformance` Changelog
|
|
2
2
|
|
|
3
|
+
## [1.156.0] — 2026-09-02 — the event codemap as data (v2 charter Phase 1, tranche 5)
|
|
4
|
+
|
|
5
|
+
One new corpus-coherence scenario file (471 → 472), not packed:
|
|
6
|
+
|
|
7
|
+
- `event-codemap-complete.test.ts` — gates on `V1_DIR`; asserts `spec/v1/event-codemap.json` (generated by `scripts/generate-event-codemap.mjs`) has exactly one row per `RunEventType`, every row's payload `$def` exists, and every proposed v2 name is unique, matches the charter's `domain.verb-ed` grammar, and never uses the reserved `core.` prefix. 117 rows; 18 flagged for a C.4 decision (tense, folded segments, `core.` drop). Registered in `SPEC_COHERENCE_SCENARIOS`, `spec-coherence-scenarios.json`, and the `files` negations; the published tarball is unchanged in content.
|
|
8
|
+
|
|
3
9
|
## [1.155.0] — 2026-09-02 — RFC 0165 `Draft → Active`: `protocolVersions[]`, the Subject record, header dual emission
|
|
4
10
|
|
|
5
11
|
Three new scenario files (468 → 471) and four added legs, all additive:
|
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ Exit code is non-zero on any failed assertion. `--certify` distinguishes: `0`
|
|
|
103
103
|
|
|
104
104
|
## What's Covered
|
|
105
105
|
|
|
106
|
-
The current suite has
|
|
106
|
+
The current suite has 472 scenario files under `src/scenarios/`.
|
|
107
107
|
- 2026-08-19 (suite `1.137.0 → 1.138.0`): NEW `durability-poison-exhaustion.test.ts` — RFC 0158 §C.8, the FIRST row of that RFC's conformance table to land. Asserts what `failure-path.test.ts` cannot: not just that deterministically failing work reaches terminal, but that attempts STOP — counted on the log, re-counted after a scaled quiet window, asserted unchanged. A host still redelivering records more. Seam-gated on the existing event-log seam (`blocked` = unobservable, not unmet) and outside every profile floor.
|
|
108
108
|
- 2026-08-19 (suite `1.136.15 → 1.137.0`): NEW `replay-fanout-suppression.test.ts` — capability-gated on `webhooks.supported`, **outside every profile floor**; witnesses `replay.md` §"Host-initiated fan-out is an external effect", which was the largest normative MUST NOT on the replay surface with no scenario and no SECURITY invariant. Three legs in ONE `it` against ONE receiver and ONE subscription — a positive control, the MUST NOT, and a `branch` boundary leg — because "no delivery arrived" passes identically when delivery never worked, so absence is asserted only after presence is proven on that exact wiring. A host with an SSRF guard correctly refuses the loopback receiver and records `blocked`: **unobservable, not unmet.**
|
|
109
109
|
- 2026-08-18 (suite `1.136.7 → 1.136.8`, SP-04): NEW `spec-section-citations.test.ts` — server-free; a `<doc>.md §"<Section>"` citation of a checked doc MUST resolve to a heading that exists. `storage-adapters.md §"Claim acquisition"` was cited by four artifacts, including a normative MUST in `production-profile.md` §Durability, and the section did not exist for the life of RFC 0009. Scoped to the docs whose section citations carry the durability contract; a corpus-wide sweep finds ~260 unresolved citations that need triage before they can gate.
|
|
@@ -447,7 +447,7 @@ Server-required (added in 1.7.0):
|
|
|
447
447
|
| ------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
448
448
|
| **Redaction** | [`capabilities.md`](../spec/v1/capabilities.md) §"Secrets" + NFR-7 + §"aiProviders" | Vendor-neutral assertions that the server doesn't leak secret material. Three scenario groups: (a) discovery shape contract — `secrets` + `aiProviders` advertisements are well-formed regardless of `secrets.supported`; when `supported === true`, scopes MUST be non-empty + `resolution === 'host-managed'`; `byok ⊆ supported`. (b) bearer-token redaction — invalid Bearer canary in `Authorization` header is not echoed in the 401 response body. (c) credentialRef echo control — gated on `secrets.supported === true`; canary planted in `configurable.ai.credentialRef` MUST NOT appear in any RunEvent payload (poll-based capture; transport-agnostic). Uses runtime-built canary fixtures (`lib/canaries.ts`) that defeat static secret scanners. 6 scenarios. |
|
|
449
449
|
|
|
450
|
-
Current source tree:
|
|
450
|
+
Current source tree: 472 scenario files. Use [`coverage.md`](./coverage.md) for current grade/gap tracking.
|
|
451
451
|
|
|
452
452
|
## Remaining Gaps
|
|
453
453
|
|
package/coverage.md
CHANGED
|
@@ -115,6 +115,7 @@ The scenario groups in the table below (one row per group; count the rows — th
|
|
|
115
115
|
| `protocol-versions-array.test.ts` | RFC 0165 §A (`version-negotiation.md` §"`protocolVersions[]`") | A (server-free: schema declares the array with the scalar's grammar) + B (presence-gated: items match, unique, contain `protocolVersion`; profile derivation unchanged) | host-pending (presence-gated) | A host that omits the array records `inapplicable`. |
|
|
116
116
|
| `owner-subject-shape.test.ts` | RFC 0165 §B.1–§B.2 (`schemas/subject.schema.json`; both `owner` sub-objects) | Server-free: subject compiles; legacy + workload forms accepted; email/whitespace `subjectId` rejected (invariant `subject-record-opaque`); keyClass iff saml/scim; both owner sub-objects declare `subject` and the echo declares `principalKind` | host-pass (server-free) | Always runs; no host needed. |
|
|
117
117
|
| `owner-subject-echo.test.ts` | RFC 0165 §B.2, §B.4, §B.5 (`auth.md` §"The Subject record", `replay.md` §"Fork ownership") | B (gated on a snapshot carrying `owner.subject`): tenant/subjectId/kind/keyClass consistency, actor depth ≤ 4, `run.started` echo equals the snapshot, fork copies tenant + subject verbatim | host-pending (gated) | A host that does not emit `owner.subject` records `inapplicable`. |
|
|
118
|
+
| `event-codemap-complete.test.ts` | `spec/v1/event-codemap.json` (v2 charter C.4/C.9 — the v1→v2 event-type codemap as data) | Corpus coherence (gates on `V1_DIR`; asserts nothing about a host): every `RunEventType` has one row, every payload `$def` exists, v2 names unique + grammatical + never `core.`-prefixed | inapplicable to hosts | Not packed (SPEC_COHERENCE_SCENARIOS). |
|
|
118
119
|
| `cross-workspace-isolation.test.ts` | RFC 0048 §C/§D (`auth.md` §Identity claims, `rest-endpoints.md` `run_forbidden`) | A (owner-echo shape if a sample run is readable; §D isolation MUST-NOT via optional `POST /v1/host/sample/identity/cross-workspace-read` seam — cross-workspace read fails closed with `run_forbidden`/`not_found`) | `host-pending` | Behavioral probe soft-skips on 404; no host advertises run ownership yet (RFC 0048 `Draft`). |
|
|
119
120
|
| `authorization-roles-shape.test.ts` | `capabilities.authorization` (RFC 0049 §A, `auth.md` §"Role-based authorization") | A (advertisement shape always — `supported` boolean; `failClosed` const true; every `roles[].role` non-empty + `scopes` array) | `host-pending` | Always runs; asserts the block is absent or well-formed. |
|
|
120
121
|
| `authorization-fail-closed.test.ts` | `capabilities.authorization` (RFC 0049 §C) + `SECURITY/invariants.yaml` `authorization-fail-closed` | A (advertisement `failClosed===true` always; fail-closed MUST-NOT via optional `POST /v1/host/sample/authorization/decide` seam — an unseeded-role principal resolves `allowed:false`) | `host-pending` | Capability-gated on `authorization.supported`; behavioral probe soft-skips on 404. Scope-match + denial-audited scenarios deferred to a host. |
|
|
@@ -93,6 +93,7 @@ export const SPEC_COHERENCE_SCENARIOS = new Set([
|
|
|
93
93
|
'effect-identity-composition.test.ts',
|
|
94
94
|
'effect-identity-cross-scope.test.ts',
|
|
95
95
|
'error-envelope-canonical-shape.test.ts',
|
|
96
|
+
'event-codemap-complete.test.ts',
|
|
96
97
|
'form-content-packs.test.ts',
|
|
97
98
|
'multi-region-effect-vocabulary.test.ts',
|
|
98
99
|
'normative-example-extraction.test.ts',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwop/openwop-conformance",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.157.0",
|
|
4
4
|
"description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"!src/scenarios/effect-identity-composition.test.ts",
|
|
41
41
|
"!src/scenarios/effect-identity-cross-scope.test.ts",
|
|
42
42
|
"!src/scenarios/error-envelope-canonical-shape.test.ts",
|
|
43
|
+
"!src/scenarios/event-codemap-complete.test.ts",
|
|
43
44
|
"!src/scenarios/form-content-packs.test.ts",
|
|
44
45
|
"!src/scenarios/multi-region-effect-vocabulary.test.ts",
|
|
45
46
|
"!src/scenarios/normative-example-extraction.test.ts",
|
package/requirements.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"$comment": "GENERATED by conformance/scripts/generate-requirement-registry.mjs — do not edit. One record per it()/test() in src/scenarios. Ids: openwop.it.<file-stem>.<title-slug>[~n] (src/lib/requirement-ids.ts). A record with id null has an interpolated title; its run-time row is keyed by the rendered title and maps here by file+line only. Renamed ids need a row in requirement-aliases.json.",
|
|
3
3
|
"generatedFrom": "src/scenarios/*.test.ts",
|
|
4
4
|
"counts": {
|
|
5
|
-
"files":
|
|
6
|
-
"tests":
|
|
7
|
-
"withStableId":
|
|
5
|
+
"files": 472,
|
|
6
|
+
"tests": 1971,
|
|
7
|
+
"withStableId": 1948,
|
|
8
8
|
"interpolatedTitles": 23,
|
|
9
9
|
"explicitIds": 0
|
|
10
10
|
},
|
|
@@ -9813,6 +9813,30 @@
|
|
|
9813
9813
|
}
|
|
9814
9814
|
]
|
|
9815
9815
|
},
|
|
9816
|
+
{
|
|
9817
|
+
"id": "openwop.it.event-codemap-complete.every-runeventtype-enum-member-has-exactly-one-row-and-every-row-names-an-enum-m",
|
|
9818
|
+
"file": "event-codemap-complete.test.ts",
|
|
9819
|
+
"line": 43,
|
|
9820
|
+
"title": "every RunEventType enum member has exactly one row, and every row names an enum member",
|
|
9821
|
+
"explicitId": null,
|
|
9822
|
+
"citations": []
|
|
9823
|
+
},
|
|
9824
|
+
{
|
|
9825
|
+
"id": "openwop.it.event-codemap-complete.every-payloaddef-exists-in-run-event-payloads-schema-json-defs",
|
|
9826
|
+
"file": "event-codemap-complete.test.ts",
|
|
9827
|
+
"line": 51,
|
|
9828
|
+
"title": "every payloadDef exists in run-event-payloads.schema.json $defs",
|
|
9829
|
+
"explicitId": null,
|
|
9830
|
+
"citations": []
|
|
9831
|
+
},
|
|
9832
|
+
{
|
|
9833
|
+
"id": "openwop.it.event-codemap-complete.every-proposed-v2-name-is-unique-grammatical-two-segments-and-never-under-the-re",
|
|
9834
|
+
"file": "event-codemap-complete.test.ts",
|
|
9835
|
+
"line": 58,
|
|
9836
|
+
"title": "every proposed v2 name is unique, grammatical, two segments, and never under the reserved `core.` prefix",
|
|
9837
|
+
"explicitId": null,
|
|
9838
|
+
"citations": []
|
|
9839
|
+
},
|
|
9816
9840
|
{
|
|
9817
9841
|
"id": "openwop.it.eventOrdering.events-from-a-single-poll-have-non-decreasing-sequence-numbers",
|
|
9818
9842
|
"file": "eventOrdering.test.ts",
|
|
@@ -14584,7 +14608,7 @@
|
|
|
14584
14608
|
{
|
|
14585
14609
|
"id": "openwop.it.owner-subject-echo.subject-is-consistent-with-the-owner-triple-b-2-and-the-actor-chain-is-bounded-b",
|
|
14586
14610
|
"file": "owner-subject-echo.test.ts",
|
|
14587
|
-
"line":
|
|
14611
|
+
"line": 78,
|
|
14588
14612
|
"title": "subject is consistent with the owner triple (§B.2) and the actor chain is bounded (§B.5)",
|
|
14589
14613
|
"explicitId": null,
|
|
14590
14614
|
"citations": [
|
|
@@ -14617,7 +14641,7 @@
|
|
|
14617
14641
|
{
|
|
14618
14642
|
"id": "openwop.it.owner-subject-echo.run-started-echoes-the-snapshot-subject-verbatim-b-2-rfc-0048-c",
|
|
14619
14643
|
"file": "owner-subject-echo.test.ts",
|
|
14620
|
-
"line":
|
|
14644
|
+
"line": 100,
|
|
14621
14645
|
"title": "run.started echoes the snapshot subject verbatim (§B.2, RFC 0048 §C)",
|
|
14622
14646
|
"explicitId": null,
|
|
14623
14647
|
"citations": [
|
|
@@ -14634,7 +14658,7 @@
|
|
|
14634
14658
|
{
|
|
14635
14659
|
"id": "openwop.it.owner-subject-echo.a-fork-copies-owner-tenant-and-owner-subject-verbatim-onto-the-child-b-4",
|
|
14636
14660
|
"file": "owner-subject-echo.test.ts",
|
|
14637
|
-
"line":
|
|
14661
|
+
"line": 117,
|
|
14638
14662
|
"title": "a fork copies owner.tenant and owner.subject verbatim onto the child (§B.4)",
|
|
14639
14663
|
"explicitId": null,
|
|
14640
14664
|
"citations": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Provenance of this vendored schemas/ copy. See conformance/README.md §\"Resolving the contract\". Compare against the stamp in your installed @openwop/openwop-conformance to detect a stale hand-copied contract.",
|
|
3
|
-
"suiteVersion": "1.
|
|
4
|
-
"corpusCommit": "
|
|
3
|
+
"suiteVersion": "1.157.0",
|
|
4
|
+
"corpusCommit": "b3a3ea6f84fc492d70865c63c82b5e47a9d24807",
|
|
5
5
|
"files": {
|
|
6
6
|
"api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
|
|
7
7
|
"api/asyncapi.yaml": "d5ecb9ee6114582be3b1f662c84bfac9ae96dae7bacb853e461168f70a8e1c7d",
|
|
@@ -94,6 +94,7 @@ export const SPEC_COHERENCE_SCENARIOS: ReadonlySet<string> = new Set([
|
|
|
94
94
|
'effect-identity-composition.test.ts',
|
|
95
95
|
'effect-identity-cross-scope.test.ts',
|
|
96
96
|
'error-envelope-canonical-shape.test.ts',
|
|
97
|
+
'event-codemap-complete.test.ts',
|
|
97
98
|
'form-content-packs.test.ts',
|
|
98
99
|
'multi-region-effect-vocabulary.test.ts',
|
|
99
100
|
'normative-example-extraction.test.ts',
|
|
@@ -18,6 +18,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
18
18
|
import { driver } from '../lib/driver.js';
|
|
19
19
|
import { isFixtureAdvertised } from '../lib/fixtures.js';
|
|
20
20
|
import { softSkip } from '../lib/soft-skip.js';
|
|
21
|
+
import { pollUntil } from '../lib/polling.js';
|
|
21
22
|
|
|
22
23
|
const NOOP_WORKFLOW_ID = 'conformance-noop';
|
|
23
24
|
const SKIP_NO_NOOP = !isFixtureAdvertised(NOOP_WORKFLOW_ID);
|
|
@@ -102,6 +103,12 @@ describe.skipIf(SKIP_NO_NOOP)('owner-subject-echo: consistency, echo, fork (RFC
|
|
|
102
103
|
if (snap.owner?.subject === undefined) {
|
|
103
104
|
return softSkip('inapplicable', 'host does not emit owner.subject on new runs (RFC 0165 §B — optional in v1.x)');
|
|
104
105
|
}
|
|
106
|
+
// Suite 1.157.0 — `run.started` is appended when the host STARTS the run,
|
|
107
|
+
// which a conforming host may do asynchronously after the 201 (openwop-app
|
|
108
|
+
// dispatches on the next tick). Reading the log straight after the POST
|
|
109
|
+
// raced that append and failed a conforming host; wait for the run to
|
|
110
|
+
// leave `pending` first, then the event is a fact, not a timing.
|
|
111
|
+
await pollUntil(runId, (s) => s.status !== 'pending', { label: 'run left pending (run.started appended)' });
|
|
105
112
|
const started = (await events(runId)).find((e) => e.type === 'run.started');
|
|
106
113
|
expect(started, driver.describe('RFC 0048 §C', 'run.started MUST be present in the event log')).toBeDefined();
|
|
107
114
|
expect(started?.payload?.owner?.subject, driver.describe('RFC 0165 §B.2', 'run.started owner.subject MUST equal RunSnapshot.owner.subject')).toEqual(snap.owner.subject);
|