@openwop/spec-artifacts 2.42.5 → 2.42.7
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/CORPUS-STAMP.json +64 -63
- package/api/seams-v2.yaml +238 -316
- package/api/v2/asyncapi.yaml +83 -113
- package/api/v2/openapi.yaml +339 -724
- package/package.json +1 -1
- package/schemas/audit-verify-result.schema.json +2 -2
- package/schemas/v2/audit-verify-result.schema.json +2 -2
- package/schemas/v2/capabilities.schema.json +2 -5
- package/spec/v1/core-standard-manifest.json +2 -2
- package/spec/v1/gaps.json +171 -1
- package/spec/v2/README.md +34 -36
- package/spec/v2/core/capabilities.md +186 -119
- package/spec/v2/core/conformance.md +78 -27
- package/spec/v2/core/connection-packs.md +8 -3
- package/spec/v2/core/conversation.md +10 -4
- package/spec/v2/core/errors.md +30 -9
- package/spec/v2/core/events.md +116 -80
- package/spec/v2/core/form-content-packs.md +11 -13
- package/spec/v2/core/headers.md +22 -14
- package/spec/v2/core/host-services.md +22 -5
- package/spec/v2/core/idempotency.md +48 -36
- package/spec/v2/core/identity.md +110 -42
- package/spec/v2/core/interop.md +82 -21
- package/spec/v2/core/interrupt.md +79 -40
- package/spec/v2/core/node-pack-runtimes.md +18 -4
- package/spec/v2/core/oauth.md +40 -6
- package/spec/v2/core/overview.md +61 -33
- package/spec/v2/core/packs.md +53 -28
- package/spec/v2/core/persistence.md +61 -80
- package/spec/v2/core/replay.md +56 -42
- package/spec/v2/core/runs.md +198 -60
- package/spec/v2/core/security-defaults.md +57 -33
- package/spec/v2/core/tool-catalog.md +29 -6
- package/spec/v2/core/versioning.md +95 -49
- package/spec/v2/core/webhooks.md +59 -20
- package/spec/v2/core/workflow-chain-packs.md +16 -14
- package/spec/v2/corrections.json +17 -1
- package/spec/v2/corrections.schema.json +1 -1
- package/spec/v2/declaration.json +24 -7
- package/spec/v2/declaration.schema.json +10 -0
- package/spec/v2/errors.json +109 -2
- package/spec/v2/errors.schema.json +38 -0
- package/spec/v2/ext/README.md +36 -23
- package/spec/v2/ext/a2uiSurface/README.md +75 -89
- package/spec/v2/ext/brand/README.md +15 -15
- package/spec/v2/ext/canvas/README.md +15 -15
- package/spec/v2/ext/chat/README.md +15 -15
- package/spec/v2/ext/coordination/README.md +15 -15
- package/spec/v2/ext/dataIntegration/README.md +17 -20
- package/spec/v2/ext/entities/README.md +15 -15
- package/spec/v2/ext/grpc-transport/README.md +6 -8
- package/spec/v2/ext/kanban/README.md +15 -15
- package/spec/v2/ext/knowledge/README.md +15 -15
- package/spec/v2/ext/launchStudio/README.md +16 -16
- package/spec/v2/ext/messaging/README.md +15 -15
- package/spec/v2/ext/portability/README.md +3 -17
- package/spec/v2/ext/provider-idempotency/README.md +7 -10
- package/spec/v2/ext/restTransport/README.md +30 -19
- package/spec/v2/ext/sandbox-runtime-notes/README.md +7 -9
- package/spec/v2/ext/webResearch/README.md +16 -16
- package/spec/v2/profiles.json +6 -2
- package/spec/v2/release.json +2 -2
- package/spec/v2/surface-baseline.json +542 -2
package/spec/v2/core/overview.md
CHANGED
|
@@ -1,41 +1,47 @@
|
|
|
1
1
|
# OpenWOP v2 Core — Overview
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
|
|
5
5
|
## Why this exists
|
|
6
6
|
|
|
7
|
-
`spec/v2/core/` is
|
|
7
|
+
`spec/v2/core/` is what a host implements to pass the 2.0.0 floor. This document sets the reading order, states the six axioms, and holds the rules other documents only reference.
|
|
8
8
|
|
|
9
9
|
## Reading order
|
|
10
10
|
|
|
11
|
-
1. `overview.md` — axioms, §0, §0a, claim vocabulary, `ext/` rule
|
|
12
|
-
2. `versioning.md` — major negotiation, `OpenWOP-Version`, the 18 axes, release identity
|
|
13
|
-
3. `capabilities.md` —
|
|
14
|
-
4. `identity.md` — Subject, lanes, `SubjectLink`, id grammars, resume tokens
|
|
15
|
-
5. `runs.md` — create
|
|
16
|
-
6. `events.md
|
|
17
|
-
7. `interrupt.md
|
|
18
|
-
8. `persistence.md` — era key, v1 reader rule, pinned runs
|
|
19
|
-
9. `security-defaults.md
|
|
20
|
-
10. `packs.md
|
|
21
|
-
11. `conformance.md` — requirement ids, witness classes, bundle v3, seams profile
|
|
11
|
+
1. [`overview.md`](overview.md) — axioms, §0, §0a, claim vocabulary, the `ext/` rule
|
|
12
|
+
2. [`versioning.md`](versioning.md) — major negotiation, `OpenWOP-Version`, the 18 axes, release identity
|
|
13
|
+
3. [`capabilities.md`](capabilities.md) — the well-known resource, record type, closed root, derived profiles
|
|
14
|
+
4. [`identity.md`](identity.md) — Subject, lanes, `SubjectLink`, id grammars, resume tokens
|
|
15
|
+
5. [`runs.md`](runs.md) — create, get, cancel, fork; `configurable`; snapshot; owner
|
|
16
|
+
6. [`events.md`](events.md), [`errors.md`](errors.md), [`headers.md`](headers.md) — event `oneOf`, payload and error registries, `OpenWOP-*` headers
|
|
17
|
+
7. [`interrupt.md`](interrupt.md), [`idempotency.md`](idempotency.md), [`replay.md`](replay.md), [`conversation.md`](conversation.md) — run-side surfaces
|
|
18
|
+
8. [`persistence.md`](persistence.md) — era key, v1 reader rule, pinned runs
|
|
19
|
+
9. [`security-defaults.md`](security-defaults.md), [`webhooks.md`](webhooks.md), [`interop.md`](interop.md), [`host-services.md`](host-services.md), [`tool-catalog.md`](tool-catalog.md) — surface obligations, signatures, A2A and MCP, host services
|
|
20
|
+
10. [`packs.md`](packs.md), [`node-pack-runtimes.md`](node-pack-runtimes.md), [`connection-packs.md`](connection-packs.md), [`form-content-packs.md`](form-content-packs.md), [`workflow-chain-packs.md`](workflow-chain-packs.md) — pack identity, engines ceiling
|
|
21
|
+
11. [`conformance.md`](conformance.md) — requirement ids, witness classes, bundle v3, seams profile
|
|
22
22
|
|
|
23
|
-
## Axioms in force
|
|
23
|
+
## Axioms in force
|
|
24
24
|
|
|
25
25
|
1. A MUST without a witness class is not a requirement.
|
|
26
|
-
2. One name per thing
|
|
27
|
-
3. Closed by default
|
|
28
|
-
4. Registers are data
|
|
29
|
-
5. Security defaults are obligations of the surface
|
|
30
|
-
6. Nothing persisted under v1 is orphaned
|
|
26
|
+
2. One name per thing. Every alias has a removal date in `spec/v1/deprecations.json` and a codemod id.
|
|
27
|
+
3. Closed by default. The discovery root, event envelope, payload and error registries, bundle, and `configurable` are `additionalProperties: false`. Vendor extension is one positive pattern in one namespace.
|
|
28
|
+
4. Registers are data. Gaps, risks, deprecations, migrations, witness classes, and dispositions are files with schemas and gates; prose is checked against them, never the reverse.
|
|
29
|
+
5. Security defaults are obligations of the surface. A protecting behavior binds when the surface is advertised, never when a flag is set.
|
|
30
|
+
6. Nothing persisted under v1 is orphaned. Every v1 artifact has a disposition in the migration register.
|
|
31
31
|
|
|
32
|
-
## §0 Closed-enum growth rule
|
|
32
|
+
## §0 Closed-enum growth rule
|
|
33
33
|
|
|
34
|
-
A registry-backed enum (event types, error codes, envelope kinds, reason vocabularies, lanes) grows by adding a row to its registry and regenerating.
|
|
34
|
+
A registry-backed enum (event types, error codes, envelope kinds, reason vocabularies, lanes) grows by adding a row to its registry and regenerating.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
- Consumers MUST accept an unknown member of a registry-backed enum and MUST NOT act on it.
|
|
37
|
+
- Producers MUST NOT emit an unregistered member.
|
|
38
|
+
- Adding a member is additive in v2.x. Renaming one is a major; removing one is a major except under §0a.
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
## §0a Retiring a v2 surface
|
|
41
|
+
|
|
42
|
+
A 2.x minor MUST NOT change the shape of an existing v2 surface; a new shape is a new surface added beside the old one.
|
|
43
|
+
|
|
44
|
+
A 2.x minor MAY remove a surface only when `scripts/check-v2-retirement.mjs` proves all of the following; otherwise the removal waits for 3.0:
|
|
39
45
|
|
|
40
46
|
1. Its replacement shipped in an earlier 2.x minor.
|
|
41
47
|
2. A `v2-minor` row in `spec/v1/deprecations.json` named the removal minor at least two minors and 30 days earlier.
|
|
@@ -46,26 +52,48 @@ A 2.x minor MUST NOT change the shape of an existing v2 surface; a new shape is
|
|
|
46
52
|
|
|
47
53
|
Readers MUST keep accepting a retired shape on replay, fork and poll; only emission narrows.
|
|
48
54
|
|
|
49
|
-
## v1 end-of-support
|
|
55
|
+
## v1 end-of-support
|
|
56
|
+
|
|
57
|
+
v1 support ends at the later of:
|
|
58
|
+
|
|
59
|
+
- (a) every INTEROP-MATRIX host's non-vacuous v2 bundle, plus 90 days;
|
|
60
|
+
- (b) 18 months from the v2 release — applied if and only if an independent host is in the matrix at release.
|
|
61
|
+
|
|
62
|
+
The date is computed from the matrix; nothing else MAY set it.
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
- **Counted hosts.** Those with a row in the INTEROP-MATRIX v2 table. A reference host that stays on the 1.x line through the overlap (the matrix says which) is not a v2 host and does not count.
|
|
65
|
+
- **Non-vacuous.** At least one claimed profile carries `witnessCount ≥ 1`.
|
|
66
|
+
- **Anchor.** The date the host's signed bundle was committed to `evidence/v2-host-bundles/` in the spec repository, read from the public history (`git log --diff-filter=A`). Never `generatedAt` inside the bundle, which nothing signs. A later re-certification replaces the file and does not move the anchor.
|
|
67
|
+
- **The computed date.** `evidence/v1-end-of-support.json`, GENERATED by `scripts/generate-v1-eos-clock.mjs`. `check-removal-dates.mjs` reads it and fails the v1-tree sources of every `v1-end-of-support` row on or after it.
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
### Old-major retention floors
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
Every published old-major artifact MUST remain installable at its last 1.x version for 12 months from the 2.0.0 publish (the `v2.0.0` tag's commit date). This holds independent of v1 end-of-support, which may come first. A consumer pinned to the old major MUST be able to rebuild through that window.
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
The artifacts are the npm packages `@openwop/openwop` (1.x) and `@openwop/openwop-conformance` (1.x), the PyPI package `openwop-client` (1.x), and the Go module `github.com/openwop/openwop-sdks/go` (v1.x). Their identities and last 1.x versions are in `spec/v2/retention-floors.json`. `scripts/check-retention-floors.mjs` prints the floor state and, with `--network`, probes each registry for the pinned version.
|
|
74
|
+
|
|
75
|
+
Unpublishing, deprecating-with-removal, or retracting a listed version inside the window is a Phase 5 exit failure.
|
|
76
|
+
|
|
77
|
+
## Profile claim vocabulary
|
|
78
|
+
|
|
79
|
+
These rules bind any public conformance statement:
|
|
58
80
|
|
|
59
81
|
- An unqualified "OpenWOP conformant" or "OpenWOP compatible" statement MUST mean `openwop-core-standard`, the executable floor, never the discovery predicate.
|
|
60
82
|
- A discovery-only claim MUST say `openwop-discovery-core` and MUST NOT use the same badge as `openwop-core-standard`.
|
|
61
83
|
- Every claim MUST state every additional profile it relies on; an omitted profile is an unclaimed one.
|
|
62
|
-
- A certification bundle MUST name canonical profile ids; `openwop-core` is deleted (see `capabilities.md`).
|
|
84
|
+
- A certification bundle MUST name canonical profile ids; `openwop-core` is deleted (see [`capabilities.md`](capabilities.md)).
|
|
63
85
|
- A vendor extension MUST NOT use an `openwop-*` id without an accepted RFC.
|
|
64
86
|
|
|
65
|
-
## What is `ext/`
|
|
87
|
+
## What is `ext/`
|
|
88
|
+
|
|
89
|
+
`spec/v2/core/` stays within the word budget that `scripts/check-core-budget.mjs` enforces; `spec/v2/ext/` holds the rest.
|
|
90
|
+
|
|
91
|
+
- Every `spec/v2/ext/<key>/` document MUST declare `witness` and both maturity axes (`technical`, `adoption`) in its header.
|
|
92
|
+
- A MUST with `witness: unwitnessable` MUST NOT appear in `core/`. A document whose only witness is "deferred to Active → Accepted" enters `ext/` or is deleted.
|
|
93
|
+
- An `ext/` family is advertised only under a wire-legal witness class (see [`capabilities.md`](capabilities.md)).
|
|
66
94
|
|
|
67
|
-
|
|
95
|
+
## What a MUST means (Axiom 1)
|
|
68
96
|
|
|
69
|
-
|
|
97
|
+
Every MUST, SHOULD, and MAY in `core/` is a requirement with an id in `requirements.json` and a `witness` from `witnessable-unaided | witnessable-gated | seam-gated | claims-check | negative-existence`. A seam-gated MUST is governed by [`conformance.md`](conformance.md) §"Witness class".
|
|
70
98
|
|
|
71
|
-
|
|
99
|
+
*Sources: RFC 0155, RFC 0167, RFC 0168, RFC 0169, RFC 0171, RFC 0174, RFC 0190, RFC 0197.*
|
package/spec/v2/core/packs.md
CHANGED
|
@@ -1,39 +1,56 @@
|
|
|
1
1
|
# Packs
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
> **Normative home:** `packs`.
|
|
5
5
|
|
|
6
6
|
## Why this exists
|
|
7
7
|
|
|
8
|
-
The v2 contract for pack manifests, the registry tree, peer-dependency identifiers, and signing. The per-kind rules live in connection-packs.md, form-content-packs.md, and workflow-chain-packs.md; the capability vocabulary a pack requires is capabilities.md.
|
|
8
|
+
The v2 contract for pack manifests, the registry tree, peer-dependency identifiers, and signing. The per-kind rules live in [connection-packs.md](connection-packs.md), [form-content-packs.md](form-content-packs.md), and [workflow-chain-packs.md](workflow-chain-packs.md); the capability vocabulary a pack requires is [capabilities.md](capabilities.md).
|
|
9
9
|
|
|
10
10
|
## The engine range
|
|
11
11
|
|
|
12
|
-
A manifest's `engines.openwop` MUST match the grammar in `schemas/v2/node-pack-manifest.schema.json`: a `>=` lower bound and an explicit `<` major ceiling (`^>=\d+(\.\d+){0,2} <\d+\.0\.0$`).
|
|
12
|
+
A manifest's `engines.openwop` MUST match the grammar in `schemas/v2/node-pack-manifest.schema.json`: a `>=` lower bound and an explicit `<` major ceiling (`^>=\d+(\.\d+){0,2} <\d+\.0\.0$`).
|
|
13
|
+
|
|
14
|
+
- A v2 host MUST treat a range with no upper bound as bounded by `<2.0.0`.
|
|
15
|
+
- A host MUST refuse to install a version whose range does not admit the host's protocol major with `pack_engine_unsupported` (`spec/v2/errors.json`).
|
|
16
|
+
- `pack_runtime_requirement_unmet` remains a runtime-requirement code and MUST NOT be used for the protocol major.
|
|
17
|
+
- Both checks MUST run at install on every publication path — the canonical registry, a vendor registry's write API, and a mirror ingest — so no registry-side artifact can bypass it.
|
|
18
|
+
|
|
19
|
+
The range is a claim about the pack's own surface, not about run semantics. Admitting major M asserts that:
|
|
20
|
+
|
|
21
|
+
- the manifest validates against the `schemas/v2/` manifest schema for its `kind`;
|
|
22
|
+
- every `peerDependencies` key resolves (§"Peer-dependency identifiers");
|
|
23
|
+
- the version carries a §Signing signature.
|
|
24
|
+
|
|
25
|
+
Each conjunct keeps its own refusal code. A mechanical ceiling bump is not a verification.
|
|
13
26
|
|
|
14
27
|
## The `packs` capability
|
|
15
28
|
|
|
16
|
-
A host advertises `packs` when it serves the registry surface above. The record
|
|
17
|
-
is the advertisement: a host MUST NOT advertise `packs` unless it resolves pack
|
|
18
|
-
references through a registry reachable from its discovery document, and a
|
|
19
|
-
client MUST treat an absent record as "this host installs no packs" rather than
|
|
20
|
-
as an unknown.
|
|
29
|
+
A host advertises `packs` when it serves the registry surface above. The record is the advertisement:
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
profile instead, and MUST NOT treat `testMode` as a second way to claim one.
|
|
31
|
+
- A host MUST NOT advertise `packs` unless it resolves pack references through a registry reachable from its discovery document.
|
|
32
|
+
- A client MUST treat an absent record as "this host installs no packs", not as an unknown.
|
|
33
|
+
|
|
34
|
+
`testMode` is DEPRECATED and MUST NOT be relied on by a client (see §"During the v1 overlap"). A host mounting a test catalog SHOULD advertise the seams profile instead, and MUST NOT treat `testMode` as a second way to claim one.
|
|
27
35
|
|
|
28
36
|
## The registry tree
|
|
29
37
|
|
|
30
|
-
The registry is versioned by tree, not header. It publishes `registry/v2/packs/<name>/-/<version>.{json,sbom.json,sig,tgz}` as a parallel tree of re-signed manifests with regenerated SBOMs and index
|
|
38
|
+
The registry is versioned by tree, not header. It publishes `registry/v2/packs/<name>/-/<version>.{json,sbom.json,sig,tgz}` as a parallel tree of re-signed manifests with regenerated SBOMs and index.
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
- A signed compatibility overlay MUST be rejected: signatures authorize by namespace, and a mirror re-derives the signer at ingest.
|
|
41
|
+
- `.well-known/openwop-registry.json` `endpoints` is the negotiation: it names both trees, and a client MUST resolve every registry path through it rather than construct one.
|
|
42
|
+
- `publicKey` is unversioned: keys are not protocol-versioned.
|
|
33
43
|
|
|
34
44
|
## Peer-dependency identifiers
|
|
35
45
|
|
|
36
|
-
A `peerDependencies` key MUST
|
|
46
|
+
A `peerDependencies` key MUST either:
|
|
47
|
+
|
|
48
|
+
- be a `families[].key` in `spec/v2/declaration.json` whose `anchor` is not `deleted` — equivalently a root key of the generated `schemas/v2/capabilities.schema.json`; or
|
|
49
|
+
- carry a row in `spec/v2/peer-dependency-aliases.json` (§"The alias table").
|
|
50
|
+
|
|
51
|
+
The declaration key, the peer-dependency identifier, and the capabilities.md section anchor are one identifier. A host MUST refuse a key the declaration file does not name with `pack_peer_dependency_undefined`.
|
|
52
|
+
|
|
53
|
+
Facet paths are not identifiers: a pack requires a family by its key and names facets in `peerDependenciesMeta.<family>.facets[]`.
|
|
37
54
|
|
|
38
55
|
```jsonc
|
|
39
56
|
"peerDependencies": { "aiProviders": "required" },
|
|
@@ -42,11 +59,13 @@ A `peerDependencies` key MUST be a `families[].key` in `spec/v2/declaration.json
|
|
|
42
59
|
|
|
43
60
|
## The alias table
|
|
44
61
|
|
|
45
|
-
`spec/v2/peer-dependency-aliases.json` is generated from the declaration file and the published-manifest inventory, never hand-kept
|
|
62
|
+
`spec/v2/peer-dependency-aliases.json` is generated from the declaration file and the published-manifest inventory, never hand-kept. It is how a v1-era key reaches a v2 family through the overlap. Each row is `{ alias, family, facets?, publishedUses, removalTrigger }` and covers a v1 grammar found in the wild (`host.*` dotted twins, `openwop.agents.memoryBackends`, facet paths such as `aiProviders.imageGeneration`).
|
|
63
|
+
|
|
64
|
+
A v2 host MAY resolve an alias through the table during the overlap, and MUST NOT resolve one after v1 end-of-support (`removalTrigger: v1-end-of-support`).
|
|
46
65
|
|
|
47
66
|
## The manifest schema family
|
|
48
67
|
|
|
49
|
-
The
|
|
68
|
+
The manifest schemas carry `$id` under `https://openwop.dev/spec/v2/`; the v1 `$id`s are immutable and served read-only.
|
|
50
69
|
|
|
51
70
|
| Schema (`schemas/v2/…`) | Author | Vendor hatch |
|
|
52
71
|
| --- | --- | --- |
|
|
@@ -56,25 +75,24 @@ The 13 manifest schemas carry `$id` under `https://openwop.dev/spec/v2/`; the v1
|
|
|
56
75
|
| `security-advisory` | registry | closed |
|
|
57
76
|
| `prompt-ref` | leaf | none |
|
|
58
77
|
|
|
59
|
-
Every pack-authored document MUST admit `patternProperties` `^(openwop-|x-|vendor\.)`. A consumer that does not recognize a hatch property MUST ignore it and MUST NOT reject the document; the value is pack-authored and therefore untrusted (security-defaults.md).
|
|
78
|
+
Every pack-authored document MUST admit `patternProperties` `^(openwop-|x-|vendor\.)`. A consumer that does not recognize a hatch property MUST ignore it and MUST NOT reject the document; the value is pack-authored and therefore untrusted ([security-defaults.md](security-defaults.md)).
|
|
60
79
|
|
|
61
80
|
## Signing
|
|
62
81
|
|
|
63
|
-
There is one signing scheme. `signing` on a version manifest (`schemas/v2/registry-version-manifest.schema.json`) is `{ keyId, scheme }`, both REQUIRED:
|
|
82
|
+
There is one signing scheme. `signing` on a version manifest (`schemas/v2/registry-version-manifest.schema.json`) is the closed object `{ keyId, scheme }`, both REQUIRED:
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| `keyId` | the signing key id; `publicKeyRef` does not exist |
|
|
69
|
-
| `method` | does not exist; a manifest carrying it fails validation |
|
|
84
|
+
- **`scheme`** MUST be `ed25519-canonical-json`: a detached 64-byte Ed25519 signature over the RFC 8785 (JCS) bytes of `pack.json` inside a deterministic tarball. The input MUST satisfy `conformance.md` §"Canonical JSON".
|
|
85
|
+
- **`keyId`** is the signing key id.
|
|
86
|
+
- A block carrying `method`, `publicKeyRef` or `signatureRef` fails validation.
|
|
70
87
|
|
|
71
|
-
The same block applies to a **bare manifest** — the `pack.json` inside the tarball, which is the document the signature covers. `signing` is OPTIONAL there, because an authoring-time `pack.json` exists before it is signed
|
|
88
|
+
The same block applies to a **bare manifest** — the `pack.json` inside the tarball, which is the document the signature covers. `signing` is OPTIONAL there, because an authoring-time `pack.json` exists before it is signed. When present it MUST be the identical closed `{ keyId, scheme }` object, on every bare manifest kind.
|
|
72
89
|
|
|
73
|
-
A verifier MUST verify the signature against the issuing registry's key for `keyId
|
|
90
|
+
- A verifier MUST verify the signature against the issuing registry's key for `keyId`, and MUST check the pack name against that key's `permittedNamespaces`.
|
|
91
|
+
- A signature over tarball bytes is not a v2 signature; such a pack MUST be re-signed, not relabeled.
|
|
74
92
|
|
|
75
93
|
## Version manifests
|
|
76
94
|
|
|
77
|
-
`kind` is REQUIRED on every version manifest and every bare manifest
|
|
95
|
+
`kind` is REQUIRED on every version manifest and every bare manifest. A deprecated version is flagged `versionDeprecated: true`; the registry continues to serve it, and a consumer MAY refuse to install it.
|
|
78
96
|
|
|
79
97
|
## The registry's own schemas
|
|
80
98
|
|
|
@@ -87,3 +105,10 @@ A registry MUST validate submissions against vendored copies of these schemas pi
|
|
|
87
105
|
| `pack_engine_unsupported` | the range does not admit the host's protocol major (install, every path) |
|
|
88
106
|
| `pack_peer_dependency_undefined` | a peer-dependency key is not a declaration-file key or an overlap alias |
|
|
89
107
|
| `pack_signature_invalid` | the signature, key, or namespace check fails |
|
|
108
|
+
|
|
109
|
+
## During the v1 overlap
|
|
110
|
+
|
|
111
|
+
- The v1 registry tree is frozen through the overlap, behind the v2 tree.
|
|
112
|
+
- `testMode` advertises the v1 `/v1/packs-test/*` mirror, a conformance seam ([conformance.md §"The seams profile"](conformance.md)). It remains advertisable through the overlap and is removed at 3.0.
|
|
113
|
+
|
|
114
|
+
*Sources: RFC 0177, RFC 0212.*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Persistence and Coexistence
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
> **Normative home:** `eventLog`.
|
|
5
5
|
|
|
6
6
|
## Why this exists
|
|
@@ -9,11 +9,16 @@ How a v2 host reads what a v1 host wrote, what happens to a run in flight at the
|
|
|
9
9
|
|
|
10
10
|
## The codemap is data
|
|
11
11
|
|
|
12
|
-
`spec/v2/event-codemap.json`, shipped in `@openwop/spec-artifacts`, is the only authority for the v1→v2 event-type mapping
|
|
12
|
+
`spec/v2/event-codemap.json`, shipped in `@openwop/spec-artifacts`, is the only authority for the v1→v2 event-type mapping. Every row is `decided`.
|
|
13
|
+
|
|
14
|
+
- A host MUST NOT carry a private mapping.
|
|
15
|
+
- A vendor-prefixed v1 type the codemap does not name MUST be read under its own name, unchanged.
|
|
16
|
+
- "Vendor-prefixed" means the first segment is an org registered in the `extensions` object of `spec/v2/declaration.json` ([events.md §Rules](events.md)). `openwop.` is the only reserved prefix. An unregistered first segment is not a vendor prefix and falls to the refusal in §"The reader rule".
|
|
17
|
+
- An org is registered by pull request against the corpus and takes effect on the `@openwop/spec-artifacts` release that carries it. A shipped entry is append-only: deregistering an org would turn every log already written under it from pass-through into refusal.
|
|
13
18
|
|
|
14
19
|
## The era key
|
|
15
20
|
|
|
16
|
-
`eventLogSchemaVersion` is the era key
|
|
21
|
+
`eventLogSchemaVersion` is the era key. It is required on every run snapshot (`schemas/v2/run-snapshot.schema.json`).
|
|
17
22
|
|
|
18
23
|
| Value | Meaning |
|
|
19
24
|
| --- | --- |
|
|
@@ -24,112 +29,92 @@ How a v2 host reads what a v1 host wrote, what happens to a run in flight at the
|
|
|
24
29
|
|
|
25
30
|
Discovery MUST advertise the value the host writes for new runs and nothing else; a host MUST hold one constant for this axis.
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
path's runs read as era `2
|
|
32
|
+
Absent stays era `2` forever; it is never backfilled.
|
|
33
|
+
|
|
34
|
+
- A host MUST NOT rewrite historical rows to add an explicit `2`, and a reader MUST NOT require one.
|
|
35
|
+
- A host with more than one creation path MUST begin stamping `3` on all of them in the same change. An unstamped path's runs read as era `2` — a silent wrong read.
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
For an era-`2` run with nothing stored, the host MUST supply `2` from the
|
|
34
|
-
absent-⇒-`2` rule rather than fail the read. The field therefore cannot falsify
|
|
35
|
-
era handling on its own; the reader and writer rules below carry the obligation.
|
|
37
|
+
The snapshot field is required on the wire, and MAY be synthesized: for an era-`2` run with nothing stored, the host MUST supply `2` from the absent-⇒-`2` rule rather than fail the read. The field alone therefore cannot falsify era handling; the reader and writer rules carry the obligation.
|
|
36
38
|
|
|
37
39
|
## The `eventLog` family
|
|
38
40
|
|
|
39
|
-
`eventLog` is the capability record by which a host advertises the era contract
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
surface of its own. Its `crossEngineOrdering` facet is a replay property and is
|
|
47
|
-
normed in replay.md §"Cross-engine ordering".
|
|
41
|
+
`eventLog` is the capability record by which a host advertises the era contract above. A host that advertises `eventLog`:
|
|
42
|
+
|
|
43
|
+
- MUST stamp `eventLogSchemaVersion` on every run it creates;
|
|
44
|
+
- MUST serve the cursor contract of [events.md §"Poll"](events.md) over that log;
|
|
45
|
+
- MUST NOT emit an event `type` the codemap does not name.
|
|
46
|
+
|
|
47
|
+
The record carries no separate storage claim: it asserts that the era key, the codemap and the poll cursor are implemented as written here. Its `crossEngineOrdering` facet is a replay property, specified in [replay.md §"Cross-engine ordering"](replay.md).
|
|
48
48
|
|
|
49
49
|
## The reader rule
|
|
50
50
|
|
|
51
51
|
A v2 host reading a run in era `2` MUST translate every event through the codemap at the storage boundary:
|
|
52
52
|
|
|
53
|
-
- `type` is mapped
|
|
53
|
+
- `type` is mapped, and the payload projected with it.
|
|
54
54
|
- `sequence` MUST be preserved verbatim, including `0`.
|
|
55
55
|
- `eventId`, `timestamp`, `causationId`, and vendor fields pass through.
|
|
56
|
-
- A type the codemap does not name and that carries no reserved vendor prefix MUST fail the read with `event_type_unmapped` (`spec/v2/errors.json`, `500`).
|
|
56
|
+
- A type the codemap does not name and that carries no reserved vendor prefix MUST fail the read with `event_type_unmapped` (`spec/v2/errors.json`, `500`).
|
|
57
|
+
- A malformed row MUST fail the read rather than default any field.
|
|
58
|
+
|
|
59
|
+
The rule binds every reader: poll, SSE, fork, replay divergence, debug bundle, summary memory.
|
|
57
60
|
|
|
58
|
-
The
|
|
61
|
+
The translation is a read projection. A host MUST NOT rewrite era-`2` rows in place. The one exception is a background backfill that stamps `3` and rewrites `type` under the same `(runId, sequence)` key: it is permitted only as an atomic per-run operation with the original preserved, because the fork prefix must stay byte-equivalent to the translated parent ([replay.md](replay.md)).
|
|
59
62
|
|
|
60
63
|
### The writer rule
|
|
61
64
|
|
|
62
|
-
The era key is fixed when the run is created and fixes the log's vocabulary for
|
|
63
|
-
the run's lifetime. An append to a run in era `2` MUST use v1 vocabulary — the
|
|
64
|
-
name the codemap maps *from*, not the v2 name it maps to. A host that upgrades
|
|
65
|
-
mid-flight MUST NOT begin writing v2 names into a log the reader translates as
|
|
66
|
-
era `2`: the reader would map an already-mapped name a second time, or fail the
|
|
67
|
-
read with `event_type_unmapped` on a name the codemap does not carry on its v1
|
|
68
|
-
side. A run created after the upgrade is era `3` and is written in v2
|
|
69
|
-
vocabulary, untranslated.
|
|
65
|
+
The era key is fixed when the run is created and fixes the log's vocabulary for the run's lifetime.
|
|
70
66
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
- An append to a run in era `2` MUST use v1 vocabulary — the name the codemap maps *from*, not the v2 name it maps to.
|
|
68
|
+
- A host that upgrades mid-flight MUST NOT begin writing v2 names into a log the reader translates as era `2`. The reader would map an already-mapped name a second time, or fail with `event_type_unmapped` on a name absent from the codemap's v1 side.
|
|
69
|
+
- A run created after the upgrade is era `3` and is written in v2 vocabulary, untranslated.
|
|
70
|
+
- A writer that emits a property a closed def cannot seat MUST mark the row with what it could not seat, so the refusal names the writer instead of surfacing as an unexplained read failure.
|
|
74
71
|
|
|
75
|
-
This binds every writer for as long as an era-`2` run stays open (§"Runs pinned
|
|
76
|
-
to v1"); its witness is `v2-era-2-append-vocabulary`.
|
|
72
|
+
This binds every writer for as long as an era-`2` run stays open (§"Runs pinned to v1"). Its witness is `v2-era-2-append-vocabulary`.
|
|
77
73
|
|
|
78
74
|
### The v1 wire of an era-`3` log
|
|
79
75
|
|
|
80
|
-
Through the overlap (
|
|
81
|
-
vocabulary, must still read on `/v1/…` exactly as before the cut.
|
|
76
|
+
Through the overlap ([versioning.md §5](versioning.md)), an era-`3` log, stored in v2 vocabulary, must still read on `/v1/…` exactly as before the cut. A host serving both majors:
|
|
82
77
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
host MUST verify that at load and, if a row folds two v1 names onto one v2 name,
|
|
87
|
-
MUST refuse to serve the v1 representation rather than guess a spelling.
|
|
88
|
-
|
|
89
|
-
A type with NO codemap row — v2-only vocabulary, anything RFC 0185/0186 seated
|
|
90
|
-
— has no v1 spelling to invert to. A host MUST emit it unchanged on the v1 read
|
|
91
|
-
path, MUST NOT drop the row, and MUST NOT refuse the read for it (RFC 0187
|
|
92
|
-
§B.1).
|
|
78
|
+
- MUST map an era-`3` log's `type` back to its v1 spelling on the v1 read path, through the same codemap row, inverted.
|
|
79
|
+
- MUST verify at load that `spec/v2/event-codemap.json` is a bijection, since only then is the inverse exact. If a row folds two v1 names onto one v2 name, the host MUST refuse to serve the v1 representation rather than guess a spelling.
|
|
80
|
+
- MUST emit a type with no codemap row (v2-only vocabulary, which has no v1 spelling) unchanged on the v1 read path, MUST NOT drop the row, and MUST NOT refuse the read for it.
|
|
93
81
|
|
|
94
82
|
### The seat
|
|
95
83
|
|
|
96
|
-
The adapter MUST sit at the storage boundary every reader passes through — the storage interface's event-list method, not a wrapper some call sites bypass.
|
|
84
|
+
- The adapter MUST sit at the storage boundary every reader passes through — the storage interface's event-list method, not a wrapper some call sites bypass.
|
|
85
|
+
- A host leg MUST name its seat in its ADR.
|
|
97
86
|
|
|
98
|
-
The seat is a
|
|
87
|
+
The seat is a claims-check ([conformance.md §Witness class](conformance.md)): it is discharged by that disclosure and by audit, never by the wire. It binds every reader, including ones the suite has no name for; `run-event.schema.json` records why passing legs do not discharge it.
|
|
99
88
|
|
|
100
89
|
## Runs pinned to v1
|
|
101
90
|
|
|
102
91
|
A non-terminal run a v2 host inherits carries `version.pinned` events naming change ids. The host MUST continue it or cancel it, never follow a pin silently.
|
|
103
92
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
| Any pinned change id is no longer implemented | The host MUST cancel the run with `run.cancelled` reason `v1_pin_unsupported` and `cancelledBy: "v2-cutover"`; the certification bundle reports the count. |
|
|
108
|
-
| Suspended on an interrupt at the cut | The run continues under the row above; its token drains per §"Everything else a v1 host persisted". |
|
|
93
|
+
- **Every pinned change id is still implemented** — the run MUST continue under the reader rule. The pin is honored verbatim and `version.pinned` is never rewritten.
|
|
94
|
+
- **Any pinned change id is no longer implemented** — the host MUST cancel the run with `run.cancelled` reason `v1_pin_unsupported` and `cancelledBy: "v2-cutover"`. The certification bundle reports the count.
|
|
95
|
+
- **Suspended on an interrupt at the cut** — the run continues under the rules above; its token drains per §"Everything else a v1 host persisted".
|
|
109
96
|
|
|
110
|
-
|
|
97
|
+
Multi-region skew is read-side only: after the cut, a v2 region MUST NOT accept an era-`2` write for a run it has already stamped `3`. Discovery's `minClientVersion` rule is in [versioning.md §1.5](versioning.md).
|
|
111
98
|
|
|
112
99
|
## Everything else a v1 host persisted
|
|
113
100
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
| Owner stamps | A run without a Subject MUST be legacy-stamped at first v2 read and MUST NOT be rewritten later (RFC 0170 §A.3). A host's stored owner fields are projected to the Subject; the projection is the host's to name. |
|
|
121
|
-
| Audit log | Never upgraded (RFC 0170). |
|
|
101
|
+
- **Certification bundles** — never upgraded. A v1 bundle substantiates no new certification after 2026-11-10; every host produces a fresh v2-rc bundle before the cut ([conformance.md](conformance.md)).
|
|
102
|
+
- **Webhook deliveries** — dual-emitted, and queued deliveries drained, per [webhooks.md §"Dual emission through the overlap"](webhooks.md).
|
|
103
|
+
- **Interrupt resume tokens** — drained. A token that is not `ow2.`-prefixed is a v1 token and resolves under `kid: legacy` until `expiresAt`; new tokens carry the `ow2.` prefix ([identity.md](identity.md)). The rule is written over the prefix, never a segment count.
|
|
104
|
+
- **Layer-1 and Layer-2 records** (idempotency, idempotent responses, invocation claims and logs, effect-escape ledger, dispatch outbox, envelope correlations) — unchanged; keyed on ids the cut does not rename. `GET /runs/{runId}/effects` and `GET /runs/{runId}/compensation` are new reads over them ([security-defaults.md](security-defaults.md)).
|
|
105
|
+
- **Owner stamps** — a run without a Subject MUST be legacy-stamped at first v2 read and MUST NOT be rewritten later. A host's stored owner fields are projected to the Subject; the projection is the host's to name.
|
|
106
|
+
- **Audit log** — never upgraded.
|
|
122
107
|
|
|
123
108
|
## Per-store disposition
|
|
124
109
|
|
|
125
|
-
A host's ADR MUST name every store it persists and give each one disposition from the closed set below.
|
|
110
|
+
A host's ADR MUST name every store it persists and give each one disposition from the closed set below. A host MUST NOT decide a store's disposition during the migration.
|
|
126
111
|
|
|
127
112
|
| Disposition | Meaning |
|
|
128
113
|
| --- | --- |
|
|
129
114
|
| `unchanged` | Rows keep their shape and keys; a v2 reader consumes them as they are. |
|
|
130
|
-
| `translated` |
|
|
131
|
-
| `drained` | Rows complete under their own v1 contract until exhausted or expired; no new
|
|
132
|
-
| `legacy-stamped` | A missing v2 field
|
|
115
|
+
| `translated` | Read through the codemap at the storage boundary; rewritten in place only by the atomic per-run backfill. |
|
|
116
|
+
| `drained` | Rows complete under their own v1 contract until exhausted or expired; no new v1-shaped rows are written. |
|
|
117
|
+
| `legacy-stamped` | A missing v2 field gets its legacy value at first v2 read and is never rewritten. |
|
|
133
118
|
| `never-upgraded` | Rows remain v1 evidence only; v2 evidence is produced fresh. |
|
|
134
119
|
| `not-persisted` | Nothing to migrate. |
|
|
135
120
|
|
|
@@ -146,20 +131,16 @@ Template — one row per store:
|
|
|
146
131
|
| certification bundles | v1 bundles | `never-upgraded` |
|
|
147
132
|
| host-internal tables | outside the wire | `unchanged` |
|
|
148
133
|
|
|
149
|
-
The reference hosts' dispositions are recorded in RFC 0176; a host MUST NOT decide a store's disposition during the migration.
|
|
150
|
-
|
|
151
134
|
## Durable acceptance and recovery
|
|
152
135
|
|
|
153
|
-
|
|
136
|
+
- **Acceptance** — a host that returns success for work it accepted MUST have made the intent to perform it durable in the same transaction as the work record. A wakeup, hint, or in-process dispatch MUST NOT be the only record. The host MUST resume accepted-but-unstarted work after the accepting process dies, without client action.
|
|
137
|
+
- **Liveness** — a host MUST distinguish how long a unit of work may legitimately run from the interval in which a live worker demonstrates liveness, and MUST NOT use the duration bound as the sole liveness signal.
|
|
138
|
+
- **Recovery bound** — a host MUST declare the longest interval between an instance ceasing to make progress and another becoming eligible to resume its work. It MUST derive the bound from the mechanism that enforces it: one bound per enforcing mechanism, never a single aggregate. Any length is conformant; an undeclared or unenforced bound is not.
|
|
139
|
+
- **Duplicates** — duplicate delivery of accepted work MUST NOT produce duplicate external effects. The host MUST dedupe on an identity that survives redelivery ([idempotency.md](idempotency.md)).
|
|
140
|
+
- **Poison work** — work that fails deterministically MUST reach a terminal, operator-visible state within a bounded number of attempts.
|
|
154
141
|
|
|
155
|
-
|
|
156
|
-
| --- | --- |
|
|
157
|
-
| Acceptance (§A) | A host that returns success for work it accepted MUST have made the intent to perform it durable in the same transaction as the work record; a wakeup, hint, or in-process dispatch MUST NOT be the only record. It MUST resume accepted-but-unstarted work after the accepting process dies, without client action. |
|
|
158
|
-
| Liveness (§B.3) | A host MUST distinguish how long a unit of work may legitimately run from the interval in which a live worker demonstrates liveness, and MUST NOT use the duration bound as the sole liveness signal. |
|
|
159
|
-
| Recovery bound (§B.4–6) | A host MUST declare the longest interval between an instance ceasing to make progress and another becoming eligible to resume its work, and MUST derive it from the mechanism that enforces it — one bound per enforcing mechanism, never a single aggregate. Any length is conformant; an undeclared or unenforced bound is not. |
|
|
160
|
-
| Duplicates (§C.7) | Duplicate delivery of accepted work MUST NOT produce duplicate external effects; the host MUST dedupe on an identity that survives redelivery (idempotency.md). |
|
|
161
|
-
| Poison work (§C.8) | Work that fails deterministically MUST reach a terminal, operator-visible state within a bounded number of attempts. |
|
|
142
|
+
A host MAY claim a qualification rung (`durable-single-instance`, `durable-multi-instance`, `multi-region-qualified`; cumulative) only with the evidence [RFC 0158 §D](https://github.com/openwop/openwop/blob/main/RFCS/0158-durable-execution-and-disaster-recovery-qualification.md) names for it, and MUST NOT claim one from tests in which no process was terminated. A rung is evidence, not a capability: discovery carries none, and the certification bundle publishes the rung and its bounds ([conformance.md §Bundle v3](conformance.md)).
|
|
162
143
|
|
|
163
|
-
|
|
144
|
+
See also: [overview.md](overview.md), [events.md](events.md), [replay.md](replay.md), [identity.md](identity.md), [webhooks.md](webhooks.md).
|
|
164
145
|
|
|
165
|
-
|
|
146
|
+
*Sources: RFC 0041, RFC 0158, RFC 0170, RFC 0171, RFC 0172, RFC 0176, RFC 0180, RFC 0185, RFC 0186, RFC 0187.*
|