@xemahq/xema 0.52.0 → 0.53.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/edition/README.md CHANGED
@@ -147,7 +147,7 @@ The edition library builds, validates and verifies evidence statements; it never
147
147
 
148
148
  ## Kernel protocol and pins
149
149
 
150
- The CLI is coupled to the kernel's edition protocol, not to the kernel package. `@xemahq/contracts` exports the protocol identity as data (`dist/edition-protocol/edition-protocol.json`: `{contentDigest, provisional, version}`). This CLI supports protocols v28, v30, v31, v32, v33, v34, v35 and v36 (no published kernel declares v29; a CLI lists a protocol only while every kernel published under it exports what the CLI imports; v27 and older kernels lack `serviceImageSubjects`, so a data root whose pinned kernel speaks v27 or older stays on an earlier CLI), pinned to its digest and vendored, and REFUSES an identity with `provisional: true`; every loader that executes the kernel, `xema edition check` first, and the evidence verifier compare only that identity and refuse an unsupported one, naming the supported and the declared versions. A protocol change needs a CLI release first; only then does the data root repin its kernel authority.
150
+ The CLI is coupled to the kernel's edition protocol, not to the kernel package. `@xemahq/contracts` exports the protocol identity as data (`dist/edition-protocol/edition-protocol.json`: `{contentDigest, provisional, version}`). This CLI supports protocols v28, v30, v31, v32, v33, v34, v35, v36, v37, v38 and v38 (no published kernel declares v29; a CLI lists a protocol only while every kernel published under it exports what the CLI imports; v27 and older kernels lack `serviceImageSubjects`, so a data root whose pinned kernel speaks v27 or older stays on an earlier CLI), pinned to its digest and vendored, and REFUSES an identity with `provisional: true`; every loader that executes the kernel, `xema edition check` first, and the evidence verifier compare only that identity and refuse an unsupported one, naming the supported and the declared versions. A protocol change needs a CLI release first; only then does the data root repin its kernel authority.
151
151
 
152
152
  A develop edition does not take the kernel's develop head blindly: `xema edition develop choose-kernel --kernel <package dir> [--kernel-pin <40-hex>] --out <file>` reads the declared protocol of each commit on the checkout's first-parent history (from source, unbuilt) and chooses the newest one this CLI supports. It writes `{chosenSha, headSha, held, reason}` (`reason`: `head-supported` | `head-ahead` | `explicit-pin`; `held` is `head-ahead`), and when held prints `kernel held at v<N> (<sha12>), head at v<M> (<sha12>): raise the CLI` (stdout only, never persisted). The head is the checkout's `refs/remotes/origin/develop` (a full-history checkout). An explicit pin wins but is still checked, and must be on that head's first-parent history (`not-on-develop` otherwise); when no commit is supported it refuses (`EditionProtocolMismatchError`, `no-supported-commit`; the walk ends at the first commit with no declaration file) and never falls back to the head; a declaration that exists but does not parse, at the head or any walked commit, is refused by name (`unparseable`), never skipped. `freeze-graph` applies the same check to `--kernel-sha`.
153
153
 
@@ -34,7 +34,8 @@ import { readBoundedNoFollowFile } from './no-follow-file.mjs';
34
34
  * No published kernel declares v29 (0.110.2 is v28, 0.110.3 is v30, 0.111.0 is v31, XEMA-2070 kernel is v32; v33 is the kernel's
35
35
  * bundle-signer protocol (`bundleSigner` on every OCI bundle row); v34 is the kernel's output-mapping, JSON Pointer and
36
36
  * handoff-repair-turns closure additions (XEMA-2122); v35 is the kernel's independent releases on a resolved deployment (XEMA-2244);
37
- * v36 is the kernel's JobFailureReason workflow closure addition (XEMA-2282); v33 to v36 are not yet published: their identities and contracts are captured from the
37
+ * v36 is the kernel's JobFailureReason workflow closure addition (XEMA-2282);
38
+ * v37 is the kernel's DistributionLock.catalogueContentDigest and effective-timeout wording (XEMA-2254, XEMA-2282); v38 is the kernel's optionalPeers service-manifest field (XEMA-2315); v33 to v38 are not yet published: their identities and contracts are captured from the
38
39
  * linked kernel), so v29 is not listed either. */
39
40
  export const SUPPORTED_EDITION_PROTOCOLS = Object.freeze([
40
41
  Object.freeze({ version: 28, contentDigest: 'f7795478b1bfe9a41bcbea9efe496011fcd209a56c90294029a08801e84cf92c' }),
@@ -45,6 +46,8 @@ export const SUPPORTED_EDITION_PROTOCOLS = Object.freeze([
45
46
  Object.freeze({ version: 34, contentDigest: '370ceef5956d66035265ddbf7b9c46aa686cd10c1e95171c806c0d590ef5f49c' }),
46
47
  Object.freeze({ version: 35, contentDigest: '3e616e532c45ecbd6277dde31c0d9c12a12139abe06cc0f56329d1ffe9813f8d' }),
47
48
  Object.freeze({ version: 36, contentDigest: 'b6b0c8ec3fc4182c6c3b623685af6293cfed1321f8d7c1891a83652d864c3df0' }),
49
+ Object.freeze({ version: 37, contentDigest: 'f10adca1ca5b6f51f3971756261032eeddd60e372ebfcff06a2a9d8a796d0fef' }),
50
+ Object.freeze({ version: 38, contentDigest: 'f670cb922c989fd40ac03703a612e7a6df2937233a1a20a394a859ec835d47d4' }),
48
51
  ]);
49
52
 
50
53
  /** Where a kernel package publishes its identity and its contract, package-relative. */
@@ -11,7 +11,8 @@
11
11
 
12
12
  import { readFileSync } from 'node:fs';
13
13
  import { join, resolve } from 'node:path';
14
- import { pathToFileURL } from 'node:url';
14
+
15
+ import { KernelContractAuthoritySchema } from '@xemahq/contracts/distribution';
15
16
 
16
17
  import { readKernelEditionProtocol } from './edition-protocol.mjs';
17
18
  import {
@@ -45,30 +46,11 @@ const readVersion = (packageJsonPath) => {
45
46
  return version;
46
47
  };
47
48
 
48
- /**
49
- * The bundled kernel's own strict `KernelContractAuthoritySchema` (not re-exported by its distribution index), read
50
- * from the package this CLI ships.
51
- */
52
- async function loadKernelContractAuthoritySchema(kernelDir) {
53
- const module = join(kernelDir, 'dist', 'distribution', 'lib', 'kernel-contract-authority.js');
54
- let loaded;
55
- try {
56
- loaded = await import(pathToFileURL(module).href);
57
- } catch (error) {
58
- throw new Error(`${KERNEL_PACKAGE_NAME} at ${kernelDir} has no loadable kernel contract authority schema (${module}): ${error.message}`);
59
- }
60
- const schema = loaded.KernelContractAuthoritySchema ?? loaded.default?.KernelContractAuthoritySchema;
61
- if (typeof schema?.safeParse !== 'function') {
62
- throw new Error(`${KERNEL_PACKAGE_NAME} at ${kernelDir} exports no KernelContractAuthoritySchema from ${module}`);
63
- }
64
- return schema;
65
- }
66
-
67
49
  /**
68
50
  * The contract authority of a `--contract-authority` file, proved to be the kernel this CLI bundles (`kernelDir`),
69
51
  * and that kernel's distribution schemas. An authority whose contracts digest is not the bundled one is refused
70
52
  * first, naming both digests: the publisher installs the xema CLI whose bundled `@xemahq/contracts` is that authority.
71
- * The file is then strict-parsed by the kernel's `KernelContractAuthoritySchema` (an unknown or missing field is
53
+ * The file is then strict-parsed by the kernel's `KernelContractAuthoritySchema` (imported from `@xemahq/contracts/distribution`, this CLI's own dependency, which is the bundled package) (an unknown or missing field is
72
54
  * refused) and returned in the schema's form, so its key order is never what a later comparison sees.
73
55
  *
74
56
  * `sourceCommit` is held to the schema's shape only: it is provenance, which the bundled package cannot prove. The
@@ -95,7 +77,7 @@ export async function readPublicContractAuthority({ path, kernelDir = kernelPack
95
77
  `install the @xemahq/xema version whose bundled ${KERNEL_PACKAGE_NAME} has digest ${JSON.stringify(contractAuthority.contentDigest)}`,
96
78
  );
97
79
  }
98
- const parsed = (await loadKernelContractAuthoritySchema(kernelDir)).safeParse(contractAuthority);
80
+ const parsed = KernelContractAuthoritySchema.safeParse(contractAuthority);
99
81
  if (!parsed.success) {
100
82
  const issues = parsed.error.issues.map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`).join('; ');
101
83
  throw new Error(`${CONTRACT_AUTHORITY_FLAG} ${path} is not a kernel contract authority: ${issues}`);
@@ -60,6 +60,7 @@ function loadKernelAudienceGrammars() {
60
60
  kernelAudienceGrammars = {
61
61
  ExactAudienceSchema: elementSchema('requiresAudiences'),
62
62
  AudienceNamespaceSchema: elementSchema('ownsAudienceNamespaces'),
63
+ PeerServiceNameSchema: elementSchema('optionalPeers'),
63
64
  };
64
65
  return kernelAudienceGrammars;
65
66
  }
@@ -162,6 +163,16 @@ export function componentOwnedAudienceNamespaces(component, where) {
162
163
  return declaredPrivilegeList(component, where, 'ownsAudienceNamespaces', AudienceNamespaceSchema, 'audience namespace');
163
164
  }
164
165
 
166
+ /**
167
+ * The peer services one platform service uses only when they are served
168
+ * (`optionalPeers`, edition protocol v38), each graded by the kernel's peer
169
+ * service name grammar. Declared INTENT only; `null` when undeclared.
170
+ */
171
+ export function componentOptionalPeers(component, where) {
172
+ const { PeerServiceNameSchema } = loadKernelAudienceGrammars();
173
+ return declaredPrivilegeList(component, where, 'optionalPeers', PeerServiceNameSchema, 'peer service name');
174
+ }
175
+
165
176
  /**
166
177
  * The event subscription clauses one workload component declares
167
178
  * (`subscribesEvents`), each graded by the kernel's clause grammar and returned
@@ -1,5 +1,5 @@
1
1
  import { isKernelCapabilityDomain } from './kernel-capability-domain.mjs';
2
- import { componentAudienceRequirements, componentOperationRequirements, componentOwnedAudienceNamespaces } from './operation-requirements.mjs';
2
+ import { componentAudienceRequirements, componentOperationRequirements, componentOptionalPeers, componentOwnedAudienceNamespaces } from './operation-requirements.mjs';
3
3
  import { compareStrings } from './string-order.mjs';
4
4
 
5
5
  /**
@@ -58,7 +58,7 @@ export function sourceOwnedCapabilityDomains(sourceManifest, where) {
58
58
  /**
59
59
  * What a platform service declares it requires and owns, as the index carries it, read from the
60
60
  * same declaration as {@link sourceOwnedCapabilityDomains} (the `xema` block of its source manifest):
61
- * `requiresOperations`, `requiresAudiences` and `ownsAudienceNamespaces`. Each is sorted, unique and
61
+ * `requiresOperations`, `requiresAudiences`, `ownsAudienceNamespaces` and `optionalPeers`. Each is sorted, unique and
62
62
  * non-empty, or no key at all. Declared INTENT: the lock admits or refuses each
63
63
  * (`admitOperationGrants`, `admitExactAudiences`).
64
64
  *
@@ -70,10 +70,12 @@ export function sourceDeclaredRequirements(sourceManifest, where) {
70
70
  const requiresOperations = componentOperationRequirements(xema, where);
71
71
  const requiresAudiences = componentAudienceRequirements(xema, where);
72
72
  const ownsAudienceNamespaces = componentOwnedAudienceNamespaces(xema, where);
73
+ const optionalPeers = componentOptionalPeers(xema, where);
73
74
  return {
74
75
  ...(requiresOperations === null ? {} : { requiresOperations }),
75
76
  ...(requiresAudiences === null ? {} : { requiresAudiences }),
76
77
  ...(ownsAudienceNamespaces === null ? {} : { ownsAudienceNamespaces }),
78
+ ...(optionalPeers === null ? {} : { optionalPeers }),
77
79
  };
78
80
  }
79
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/xema",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/xema-dev/xema-cli.git",
@@ -57,7 +57,7 @@
57
57
  "@xemahq/distribution-source-hash": "^0.2.2",
58
58
  "@xemahq/dsl": "^0.28.0",
59
59
  "@xemahq/identity-client": "^0.36.0",
60
- "@xemahq/contracts": "^0.112.0",
60
+ "@xemahq/contracts": "^0.116.0",
61
61
  "@xemahq/kernel-state-runtime": "^0.8.0",
62
62
  "@xemahq/license-verifier": "^0.2.0",
63
63
  "@xemahq/release-policy": "^17.6.0",
@@ -96,6 +96,6 @@
96
96
  "typecheck": "tsc --noEmit"
97
97
  },
98
98
  "xemaRelease": {
99
- "sourceDigest": "sha256:9162fc84741cdd67803f4fa63970156c8e28c7f818257f3768771531c4e81be9"
99
+ "sourceDigest": "sha256:86b6ce1a0758e2ed6ab8e9a2cd84c29c52f72582ff30c670e16e8e5982038894"
100
100
  }
101
101
  }