halfcode-compiler.xnl 0.2.9 → 0.3.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.
Files changed (58) hide show
  1. package/README.md +5 -3
  2. package/dist/application-assembly.d.ts +1 -1
  3. package/dist/application-assembly.js +1 -1
  4. package/dist/authoring-runtime.d.ts +12 -8
  5. package/dist/authoring-runtime.js +1 -1
  6. package/dist/{effective-content-identities-DV5fTr5Q.js → effective-content-identities-CV6XFBZy.js} +8 -8
  7. package/dist/{effective-content-identities-CSVFi-N5.d.ts → effective-content-identities-drMRhYE2.d.ts} +2 -2
  8. package/dist/{index-B515mMkO.d.ts → index-CR7wJSSX.d.ts} +10 -6
  9. package/dist/{index-Cqh9pEBa.d.ts → index-DEikbOFh.d.ts} +2 -1
  10. package/dist/{index-CF4nEkOi.d.ts → index-DI3WtuXq.d.ts} +1 -1
  11. package/dist/index-DboIOEr8.d.ts +474 -0
  12. package/dist/index.d.ts +7 -6
  13. package/dist/index.js +6 -5
  14. package/dist/kind-definition.d.ts +118 -10
  15. package/dist/kind-definition.js +826 -15
  16. package/dist/resource-core-Brj8naBr.d.ts +47 -0
  17. package/dist/resource-core.d.ts +4 -4
  18. package/dist/resource-core.js +25 -11
  19. package/dist/resource-projection.d.ts +1 -1
  20. package/dist/skill-capsule.d.ts +2 -2
  21. package/dist/skill-capsule.js +1 -1
  22. package/dist/{src-3pnDAjBD.js → src-B0Hja_dk.js} +5 -7
  23. package/dist/{src-CaQ25rMj.js → src-DLJUCH0M.js} +11 -7
  24. package/dist/{src-3MZLm339.js → src-hrUy4DJ9.js} +71 -19
  25. package/dist/{src-D3qSzGFn.js → src-jggOQtq3.js} +511 -113
  26. package/dist/system-skills/resource-dsl/KindDefinitions/SkillCapsule/manifest.xnl +15 -3
  27. package/dist/system-skills/resource-dsl/KindDefinitions/WikiPage/manifest.xnl +15 -3
  28. package/dist/system-skills/resource-dsl/SkillCapsules/System/SKILL.metadata.yaml +1 -0
  29. package/dist/system-skills/resource-dsl/SkillCapsules/System/manifest.xnl +1 -1
  30. package/dist/system-skills/resource-dsl/SkillCapsules/System/resource-mappings.xnl +1 -1
  31. package/dist/system-skills/resource-dsl/WikiPages/documents/manifest.xnl +1 -1
  32. package/dist/system-skills/resource-dsl/WikiPages/files/manifest.xnl +1 -1
  33. package/dist/system-skills/resource-dsl/WikiPages/index/manifest.xnl +1 -1
  34. package/dist/system-skills/resource-dsl/WikiPages/language/manifest.xnl +1 -1
  35. package/dist/system-skills/resource-dsl/WikiPages/migration/manifest.xnl +1 -1
  36. package/dist/system-skills/resource-dsl/WikiPages/projections/manifest.xnl +1 -1
  37. package/dist/system-skills/resource-dsl/WikiPages/skill-capsules/manifest.xnl +1 -1
  38. package/dist/system-skills/resource-dsl/documents.md +33 -17
  39. package/dist/system-skills/resource-dsl/examples/function.xnl +1 -1
  40. package/dist/system-skills/resource-dsl/examples/kind-definition.xnl +15 -3
  41. package/dist/system-skills/resource-dsl/examples/resource-mappings.xnl +1 -1
  42. package/dist/system-skills/resource-dsl/examples/resource-package.xnl +1 -1
  43. package/dist/system-skills/resource-dsl/examples/skill-capsule-authoring.xnl +1 -1
  44. package/dist/system-skills/resource-dsl/examples/skill-capsule-devops.xnl +1 -1
  45. package/dist/system-skills/resource-dsl/examples/skill-capsule-kind-definition.xnl +14 -3
  46. package/dist/system-skills/resource-dsl/examples/skill-capsule-resource-dsl.xnl +1 -1
  47. package/dist/system-skills/resource-dsl/examples/skill-capsule-run.xnl +1 -1
  48. package/dist/system-skills/resource-dsl/index.md +5 -5
  49. package/dist/system-skills/resource-dsl/language.md +10 -8
  50. package/dist/system-skills/resource-dsl/manifest.xnl +1 -1
  51. package/dist/system-skills/resource-dsl/migration.md +6 -3
  52. package/dist/system-skills/resource-dsl/projections.md +7 -6
  53. package/dist/system-skills/resource-dsl/skill-capsules.md +8 -7
  54. package/dist/system-skills/sys-halfcode-resource-dsl.plan.js +84 -59
  55. package/dist/testing.js +1 -1
  56. package/package.json +1 -1
  57. package/dist/index-CDIT65pI.d.ts +0 -192
  58. package/dist/resource-core-C0Fj-VJA.d.ts +0 -29
@@ -0,0 +1,47 @@
1
+ import { et as ResourceMetadata, i as AuthoredResourceTree, p as ResourceDiagnostic, st as ResourcePackageReadPort, yt as ResourceDigestContribution } from "./index-DboIOEr8.js";
2
+ //#region ../resource-core/src/composition-error.d.ts
3
+ declare class ResourceCompositionError extends Error {
4
+ readonly diagnostics: readonly ResourceDiagnostic[];
5
+ constructor(diagnostics: readonly ResourceDiagnostic[]);
6
+ }
7
+ //#endregion
8
+ //#region ../resource-core/src/resource-material.d.ts
9
+ interface ReadResourceMaterialInput {
10
+ readonly tree: AuthoredResourceTree;
11
+ readonly rootDir: string;
12
+ readonly resourceId: string;
13
+ readonly uri: string;
14
+ }
15
+ interface ResourceMaterial {
16
+ readonly resourceId: string;
17
+ readonly uri: string;
18
+ readonly sourceUri: string;
19
+ readonly digest: string;
20
+ readonly contribution: ResourceDigestContribution;
21
+ bytes(): Uint8Array;
22
+ }
23
+ declare class ResourceMaterialError extends Error {
24
+ readonly diagnostics: readonly ResourceDiagnostic[];
25
+ constructor(diagnostics: readonly ResourceDiagnostic[]);
26
+ }
27
+ declare function readResourceMaterial(input: ReadResourceMaterialInput): Promise<ResourceMaterial>;
28
+ //#endregion
29
+ //#region ../resource-core/src/directory-resource-package-read-port.d.ts
30
+ declare function createDirectoryResourcePackageReadPort(rootDir: string): Promise<ResourcePackageReadPort>;
31
+ //#endregion
32
+ //#region ../resource-core/src/resource-envelope.d.ts
33
+ interface ResourceMetadataDecodeInput {
34
+ readonly fields: Readonly<Record<string, unknown>>;
35
+ readonly removedFieldContainers?: readonly Readonly<Record<string, unknown>>[];
36
+ readonly lifecycle?: unknown;
37
+ readonly location: string;
38
+ readonly source: "XNL" | "Markdown";
39
+ }
40
+ type ResourceMetadataDecodeResult = Readonly<{
41
+ metadata: ResourceMetadata;
42
+ }> | Readonly<{
43
+ diagnostic: ResourceDiagnostic;
44
+ }>;
45
+ declare function decodeResourceMetadata(input: ResourceMetadataDecodeInput): ResourceMetadataDecodeResult;
46
+ //#endregion
47
+ export { ReadResourceMaterialInput as a, readResourceMaterial as c, createDirectoryResourcePackageReadPort as i, ResourceCompositionError as l, ResourceMetadataDecodeResult as n, ResourceMaterial as o, decodeResourceMetadata as r, ResourceMaterialError as s, ResourceMetadataDecodeInput as t };
@@ -1,4 +1,4 @@
1
- import { A as buildResourceDependencySnapshot, B as ResourceTombstone, C as BuildResourceDependencySnapshotInput, D as ResourceDependencySnapshot, E as ResourceDependencyEdge, F as EffectiveResourceEntry, H as composeLayeredResourceRegistry, I as EffectiveResourceRegistry, L as ResourceLayerDescriptor, M as sha256Digest, N as ComposeLayeredResourceRegistryInput, O as ResourceDigestContribution, P as EffectiveKindDefinition, R as ResourceLayerInput, S as validateResourceTree, T as ResourceContentIdentity, U as Sha256Digest, V as ResourceTombstoneOrigin, _ as ResourceValue, a as ResourceDescriptor, b as SourceShape, c as ResourceKindContract, d as ResourceRecord, f as ResourceRegistry, g as ResourceValidationError, h as ResourceTreeBuildResult, i as RegisteredKindDefinition, j as createResourceContentIdentity, k as SnapshotResource, l as ResourceMetadata, m as ResourceTree, n as LoadedResourceTree, o as ResourceDiagnostic, p as ResourceScalar, r as LoadedResourceTreeBuildResult, s as ResourceIdentity, t as LoadResourceTreeOptions, u as ResourceNode, v as ResourceValueList, w as CreateResourceContentIdentityInput, x as loadResourceTree, y as ResourceValueMap, z as ResourceOrigin } from "./index-CDIT65pI.js";
2
- import { a as ResourceCompositionError, i as readResourceMaterial, n as ResourceMaterial, r as ResourceMaterialError, t as ReadResourceMaterialInput } from "./resource-core-C0Fj-VJA.js";
3
- import { n as ResourceLayerContentIdentityInput, r as resolveEffectiveResourceContentIdentities, t as ResolveEffectiveResourceContentIdentitiesInput } from "./effective-content-identities-CSVFi-N5.js";
4
- export { BuildResourceDependencySnapshotInput, ComposeLayeredResourceRegistryInput, CreateResourceContentIdentityInput, EffectiveKindDefinition, EffectiveResourceEntry, EffectiveResourceRegistry, LoadResourceTreeOptions, LoadedResourceTree, LoadedResourceTreeBuildResult, ReadResourceMaterialInput, RegisteredKindDefinition, ResolveEffectiveResourceContentIdentitiesInput, ResourceCompositionError, ResourceContentIdentity, ResourceDependencyEdge, ResourceDependencySnapshot, ResourceDescriptor, ResourceDiagnostic, ResourceDigestContribution, ResourceIdentity, ResourceKindContract, ResourceLayerContentIdentityInput, ResourceLayerDescriptor, ResourceLayerInput, ResourceMaterial, ResourceMaterialError, ResourceMetadata, ResourceNode, ResourceOrigin, ResourceRecord, ResourceRegistry, ResourceScalar, ResourceTombstone, ResourceTombstoneOrigin, ResourceTree, ResourceTreeBuildResult, ResourceValidationError, ResourceValue, ResourceValueList, ResourceValueMap, type Sha256Digest, SnapshotResource, SourceShape, buildResourceDependencySnapshot, composeLayeredResourceRegistry, createResourceContentIdentity, loadResourceTree, readResourceMaterial, resolveEffectiveResourceContentIdentities, sha256Digest, validateResourceTree };
1
+ import { $ as RESOURCE_ENVELOPE_VERSION, A as validateResourceTreeFromReadPort, At as ResourceOrigin, B as KindSpecRevision, C as ResourceValue, Ct as sha256Digest, D as loadResourceTree, Dt as EffectiveResourceRegistry, E as SourceShape, Et as EffectiveResourceEntry, F as KindContractLockReader, Ft as canonicalJson, G as ReaderProfile, H as KindWriterRegistration, I as KindReaderRegistration, It as compareCodeUnits, J as ResourceResolutionReceipt, K as ReaderTarget, L as KindSemanticContract, Lt as digestCanonical, M as JsonSchema, Mt as ResourceTombstoneOrigin, N as JsonSchemaValidator, Nt as composeLayeredResourceRegistry, O as loadResourceTreeFromReadPort, Ot as ResourceLayerDescriptor, P as KindContractLock, Pt as Sha256Digest, Q as RESOURCE_ENVELOPE_FINGERPRINT, R as KindSourceContract, S as ResourceValidationError, St as createResourceContentIdentity, T as ResourceValueMap, Tt as EffectiveKindDefinition, U as PortableSpec, V as KindSubjectOwner, W as PortableValue, X as CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS, Y as SpecResolutionDefinition, Z as RESOURCE_ENVELOPE_CONTRACT, _ as ResourceRecord, _t as ResourceDependencyEdge, a as AuthoredResourceTreeBuildResult, at as ResourcePackageEntry, b as ResourceTree, bt as SnapshotResource, c as RegisteredKindDefinition, ct as ResourcePackageReadPortOptions, d as ResolvedResourceTree, dt as dirnameResourcePackageSourcePath, et as ResourceMetadata, f as ResourceDescriptor, ft as joinResourcePackageSourcePath, g as ResourceNode, gt as ResourceContentIdentity, h as ResourceKindContract, ht as CreateResourceContentIdentityInput, i as AuthoredResourceTree, it as ResourcePackageDirectoryEntry, j as CompatibilityPolicy, jt as ResourceTombstone, k as validateResourceTree, kt as ResourceLayerInput, l as ResolvedResourceRecord, lt as ResourcePackageSourcePathError, m as ResourceIdentity, mt as BuildResourceDependencySnapshotInput, n as AuthoredResourceRegistry, nt as isSpecVersion, o as KindSpecRevisionDescriptor, ot as ResourcePackageEntryKind, p as ResourceDiagnostic, pt as relativeResourcePackageSourcePath, q as ResolvedSpec, r as AuthoredResourceSpec, rt as ResourcePackageAwaitable, s as LoadResourceTreeOptions, st as ResourcePackageReadPort, t as AuthoredResourceRecord, tt as SpecVersion, u as ResolvedResourceRegistry, ut as canonicalResourcePackageSourcePath, v as ResourceRegistry, vt as ResourceDependencySnapshot, w as ResourceValueList, wt as ComposeLayeredResourceRegistryInput, x as ResourceTreeBuildResult, xt as buildResourceDependencySnapshot, y as ResourceScalar, yt as ResourceDigestContribution, z as KindSourceShape } from "./index-DboIOEr8.js";
2
+ import { a as ReadResourceMaterialInput, c as readResourceMaterial, i as createDirectoryResourcePackageReadPort, l as ResourceCompositionError, n as ResourceMetadataDecodeResult, o as ResourceMaterial, r as decodeResourceMetadata, s as ResourceMaterialError, t as ResourceMetadataDecodeInput } from "./resource-core-Brj8naBr.js";
3
+ import { n as ResourceLayerContentIdentityInput, r as resolveEffectiveResourceContentIdentities, t as ResolveEffectiveResourceContentIdentitiesInput } from "./effective-content-identities-drMRhYE2.js";
4
+ export { AuthoredResourceRecord, AuthoredResourceRegistry, AuthoredResourceSpec, AuthoredResourceTree, AuthoredResourceTreeBuildResult, BuildResourceDependencySnapshotInput, CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS, CompatibilityPolicy, ComposeLayeredResourceRegistryInput, CreateResourceContentIdentityInput, EffectiveKindDefinition, EffectiveResourceEntry, EffectiveResourceRegistry, JsonSchema, JsonSchemaValidator, KindContractLock, KindContractLockReader, KindReaderRegistration, KindSemanticContract, KindSourceContract, KindSourceShape, KindSpecRevision, KindSpecRevisionDescriptor, KindSubjectOwner, KindWriterRegistration, LoadResourceTreeOptions, PortableSpec, PortableValue, RESOURCE_ENVELOPE_CONTRACT, RESOURCE_ENVELOPE_FINGERPRINT, RESOURCE_ENVELOPE_VERSION, ReadResourceMaterialInput, ReaderProfile, ReaderTarget, RegisteredKindDefinition, ResolveEffectiveResourceContentIdentitiesInput, ResolvedResourceRecord, ResolvedResourceRegistry, ResolvedResourceTree, ResolvedSpec, ResourceCompositionError, ResourceContentIdentity, ResourceDependencyEdge, ResourceDependencySnapshot, ResourceDescriptor, ResourceDiagnostic, ResourceDigestContribution, ResourceIdentity, ResourceKindContract, ResourceLayerContentIdentityInput, ResourceLayerDescriptor, ResourceLayerInput, ResourceMaterial, ResourceMaterialError, ResourceMetadata, ResourceMetadataDecodeInput, ResourceMetadataDecodeResult, ResourceNode, ResourceOrigin, ResourcePackageAwaitable, ResourcePackageDirectoryEntry, ResourcePackageEntry, ResourcePackageEntryKind, ResourcePackageReadPort, ResourcePackageReadPortOptions, ResourcePackageSourcePathError, ResourceRecord, ResourceRegistry, ResourceResolutionReceipt, ResourceScalar, ResourceTombstone, ResourceTombstoneOrigin, ResourceTree, ResourceTreeBuildResult, ResourceValidationError, ResourceValue, ResourceValueList, ResourceValueMap, type Sha256Digest, SnapshotResource, SourceShape, SpecResolutionDefinition, SpecVersion, buildResourceDependencySnapshot, canonicalJson, canonicalResourcePackageSourcePath, compareCodeUnits, composeLayeredResourceRegistry, createDirectoryResourcePackageReadPort, createResourceContentIdentity, decodeResourceMetadata, digestCanonical, dirnameResourcePackageSourcePath, isSpecVersion, joinResourcePackageSourcePath, loadResourceTree, loadResourceTreeFromReadPort, readResourceMaterial, relativeResourcePackageSourcePath, resolveEffectiveResourceContentIdentities, sha256Digest, validateResourceTree, validateResourceTreeFromReadPort };
@@ -1,5 +1,5 @@
1
- import { a as isLoadedResourceTreeAuthentic, c as sha256Digest, d as markEffectiveRegistryAuthentic, f as ResourceCompositionError, i as readonlyMap, m as digestCanonical, n as loadResourceTree, o as buildResourceDependencySnapshot, p as compareCodeUnits, r as validateResourceTree, s as createResourceContentIdentity, t as ResourceValidationError } from "./src-D3qSzGFn.js";
2
- import { t as resolveEffectiveResourceContentIdentities } from "./effective-content-identities-DV5fTr5Q.js";
1
+ import { C as digestCanonical, D as dirnameResourcePackageSourcePath, E as canonicalResourcePackageSourcePath, O as joinResourcePackageSourcePath, S as compareCodeUnits, T as ResourcePackageSourcePathError, a as validateResourceTreeFromReadPort, b as ResourceCompositionError, c as RESOURCE_ENVELOPE_CONTRACT, d as isSpecVersion, f as readonlyMap, g as sha256Digest, h as createResourceContentIdentity, i as validateResourceTree, k as relativeResourcePackageSourcePath, l as RESOURCE_ENVELOPE_FINGERPRINT, m as buildResourceDependencySnapshot, n as loadResourceTree, o as decodeResourceMetadata, p as isAuthoredResourceTreeAuthentic, r as loadResourceTreeFromReadPort, s as CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS, t as ResourceValidationError, u as RESOURCE_ENVELOPE_VERSION, w as createDirectoryResourcePackageReadPort, x as canonicalJson, y as markEffectiveRegistryAuthentic } from "./src-jggOQtq3.js";
2
+ import { t as resolveEffectiveResourceContentIdentities } from "./effective-content-identities-CV6XFBZy.js";
3
3
  import { lstat, readFile, realpath } from "node:fs/promises";
4
4
  import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
5
5
  //#region ../resource-core/src/layered-registry.ts
@@ -236,9 +236,9 @@ function cloneResourceRecord(resource) {
236
236
  }
237
237
  function cloneResourceMetadata(metadata) {
238
238
  return Object.freeze({
239
- apiVersion: metadata.apiVersion,
240
- ...metadata.lifecycle === void 0 ? {} : { lifecycle: metadata.lifecycle },
241
- ...metadata.version === void 0 ? {} : { version: metadata.version }
239
+ envelopeVersion: metadata.envelopeVersion,
240
+ specVersion: metadata.specVersion,
241
+ ...metadata.lifecycle === void 0 ? {} : { lifecycle: metadata.lifecycle }
242
242
  });
243
243
  }
244
244
  function cloneResourceValue(value) {
@@ -252,10 +252,17 @@ function cloneRegisteredKindDefinition(definition) {
252
252
  return Object.freeze({
253
253
  resourceId: definition.resourceId,
254
254
  resourceKind: definition.resourceKind,
255
+ subjectFqn: definition.subjectFqn,
255
256
  sourceShapes: Object.freeze([...definition.sourceShapes].sort(compareCodeUnits)),
256
257
  requiredFiles: Object.freeze([...definition.requiredFiles].sort(compareCodeUnits)),
257
- currentApiVersion: definition.currentApiVersion,
258
- supportedApiVersions: Object.freeze([...definition.supportedApiVersions].sort(compareCodeUnits)),
258
+ specRevisions: Object.freeze(definition.specRevisions.map((revision) => Object.freeze({
259
+ specVersion: revision.specVersion,
260
+ schemaRef: revision.schemaRef,
261
+ schemaFingerprint: revision.schemaFingerprint,
262
+ contractFingerprint: revision.contractFingerprint,
263
+ semanticContract: Object.freeze({ ...revision.semanticContract }),
264
+ stability: revision.stability
265
+ })).sort((left, right) => left.specVersion - right.specVersion)),
259
266
  documentCardinality: definition.documentCardinality,
260
267
  documentUri: definition.documentUri
261
268
  });
@@ -263,10 +270,17 @@ function cloneRegisteredKindDefinition(definition) {
263
270
  function normalizedKindContract(definition) {
264
271
  return {
265
272
  resourceKind: definition.resourceKind,
273
+ subjectFqn: definition.subjectFqn,
266
274
  sourceShapes: [...definition.sourceShapes].sort(compareCodeUnits),
267
275
  requiredFiles: [...definition.requiredFiles].sort(compareCodeUnits),
268
- currentApiVersion: definition.currentApiVersion,
269
- supportedApiVersions: [...definition.supportedApiVersions].sort(compareCodeUnits),
276
+ specRevisions: definition.specRevisions.map((revision) => ({
277
+ specVersion: revision.specVersion,
278
+ schemaRef: revision.schemaRef,
279
+ schemaFingerprint: revision.schemaFingerprint,
280
+ contractFingerprint: revision.contractFingerprint,
281
+ semanticContract: revision.semanticContract,
282
+ stability: revision.stability
283
+ })),
270
284
  documentCardinality: definition.documentCardinality
271
285
  };
272
286
  }
@@ -326,7 +340,7 @@ var ResourceMaterialError = class extends Error {
326
340
  }
327
341
  };
328
342
  async function readResourceMaterial(input) {
329
- if (!isLoadedResourceTreeAuthentic(input.tree)) throw materialError("RESOURCE_MATERIAL_TREE_UNTRUSTED", input.resourceId, "Resource materials require an authentic LoadedResourceTree.");
343
+ if (!isAuthoredResourceTreeAuthentic(input.tree)) throw materialError("RESOURCE_MATERIAL_TREE_UNTRUSTED", input.resourceId, "Resource materials require an authentic AuthoredResourceTree.");
330
344
  const record = findResource(input.tree, input.resourceId);
331
345
  const identity = input.tree.contentIdentities.get(input.resourceId);
332
346
  if (!record || record.sourceShape !== "directory" || !identity) throw materialError("RESOURCE_MATERIAL_RESOURCE_INVALID", input.resourceId, `Resource '${input.resourceId}' is not an authentic directory resource.`);
@@ -400,4 +414,4 @@ function materialError(code, resourceId, message) {
400
414
  })]);
401
415
  }
402
416
  //#endregion
403
- export { ResourceCompositionError, ResourceMaterialError, ResourceValidationError, buildResourceDependencySnapshot, composeLayeredResourceRegistry, createResourceContentIdentity, loadResourceTree, readResourceMaterial, resolveEffectiveResourceContentIdentities, sha256Digest, validateResourceTree };
417
+ export { CORE_BOOTSTRAP_WRITER_SPEC_VERSIONS, RESOURCE_ENVELOPE_CONTRACT, RESOURCE_ENVELOPE_FINGERPRINT, RESOURCE_ENVELOPE_VERSION, ResourceCompositionError, ResourceMaterialError, ResourcePackageSourcePathError, ResourceValidationError, buildResourceDependencySnapshot, canonicalJson, canonicalResourcePackageSourcePath, compareCodeUnits, composeLayeredResourceRegistry, createDirectoryResourcePackageReadPort, createResourceContentIdentity, decodeResourceMetadata, digestCanonical, dirnameResourcePackageSourcePath, isSpecVersion, joinResourcePackageSourcePath, loadResourceTree, loadResourceTreeFromReadPort, readResourceMaterial, relativeResourcePackageSourcePath, resolveEffectiveResourceContentIdentities, sha256Digest, validateResourceTree, validateResourceTreeFromReadPort };
@@ -1,2 +1,2 @@
1
- import { n as resourceProjectionPackage, t as ResourceRegistry } from "./index-CF4nEkOi.js";
1
+ import { n as resourceProjectionPackage, t as ResourceRegistry } from "./index-DI3WtuXq.js";
2
2
  export { ResourceRegistry, resourceProjectionPackage };
@@ -1,3 +1,3 @@
1
- import { I as SkillCapsuleDependency } from "./index-Cqh9pEBa.js";
2
- import { S as skillCapsuleDistributionProjection, _ as applySkillCapsuleDistributionPlan, a as PlannedSkillCapsule, b as compilerSkillPackage, c as SkillCapsuleContentDigest, d as SkillCapsuleDistributionReceipt, f as SkillCapsuleIdentity, g as SkillReference, h as SkillDescriptor, i as PlanSkillCapsuleDistributionInput, l as SkillCapsuleDistributionError, m as SkillCapsuleProvenanceManifest, n as CompileResourceSkillCapsuleInput, o as PlannedSkillFile, p as SkillCapsulePlan, r as CompileSkillCapsuleInput, s as SkillCapsuleApplyStep, t as ApplySkillCapsuleDistributionOptions, u as SkillCapsuleDistributionPlan, v as compileResourceSkillCapsule, x as planSkillCapsuleDistribution, y as compileSkillCapsule } from "./index-B515mMkO.js";
1
+ import { I as SkillCapsuleDependency } from "./index-DEikbOFh.js";
2
+ import { S as skillCapsuleDistributionProjection, _ as applySkillCapsuleDistributionPlan, a as PlannedSkillCapsule, b as compilerSkillPackage, c as SkillCapsuleContentDigest, d as SkillCapsuleDistributionReceipt, f as SkillCapsuleIdentity, g as SkillReference, h as SkillDescriptor, i as PlanSkillCapsuleDistributionInput, l as SkillCapsuleDistributionError, m as SkillCapsuleProvenanceManifest, n as CompileResourceSkillCapsuleInput, o as PlannedSkillFile, p as SkillCapsulePlan, r as CompileSkillCapsuleInput, s as SkillCapsuleApplyStep, t as ApplySkillCapsuleDistributionOptions, u as SkillCapsuleDistributionPlan, v as compileResourceSkillCapsule, x as planSkillCapsuleDistribution, y as compileSkillCapsule } from "./index-CR7wJSSX.js";
3
3
  export { ApplySkillCapsuleDistributionOptions, CompileResourceSkillCapsuleInput, CompileSkillCapsuleInput, PlanSkillCapsuleDistributionInput, PlannedSkillCapsule, PlannedSkillFile, SkillCapsuleApplyStep, SkillCapsuleContentDigest, type SkillCapsuleDependency, SkillCapsuleDistributionError, SkillCapsuleDistributionPlan, SkillCapsuleDistributionReceipt, SkillCapsuleIdentity, SkillCapsulePlan, SkillCapsuleProvenanceManifest, SkillDescriptor, SkillReference, applySkillCapsuleDistributionPlan, compileResourceSkillCapsule, compileSkillCapsule, compilerSkillPackage, planSkillCapsuleDistribution, skillCapsuleDistributionProjection };
@@ -1,2 +1,2 @@
1
- import { a as compilerSkillPackage, i as compileSkillCapsule, n as applySkillCapsuleDistributionPlan, o as planSkillCapsuleDistribution, r as compileResourceSkillCapsule, s as skillCapsuleDistributionProjection, t as SkillCapsuleDistributionError } from "./src-CaQ25rMj.js";
1
+ import { a as compilerSkillPackage, i as compileSkillCapsule, n as applySkillCapsuleDistributionPlan, o as planSkillCapsuleDistribution, r as compileResourceSkillCapsule, s as skillCapsuleDistributionProjection, t as SkillCapsuleDistributionError } from "./src-DLJUCH0M.js";
2
2
  export { SkillCapsuleDistributionError, applySkillCapsuleDistributionPlan, compileResourceSkillCapsule, compileSkillCapsule, compilerSkillPackage, planSkillCapsuleDistribution, skillCapsuleDistributionProjection };
@@ -1,4 +1,4 @@
1
- import { n as loadResourceTree } from "./src-D3qSzGFn.js";
1
+ import { n as loadResourceTree } from "./src-jggOQtq3.js";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import { parse } from "yaml";
@@ -388,14 +388,11 @@ async function readTextResource(module, record, kind) {
388
388
  async function readSkillCapsule(module, record) {
389
389
  const metadataMaterial = await readRequiredMaterial(module, record, "SkillMetadata");
390
390
  const template = await readRequiredMaterial(module, record, "Template");
391
- const version = record.metadata.version;
392
- const apiVersion = record.metadata.apiVersion;
393
- if (!apiVersion) throw new Error(`SKILL_CAPSULE_API_VERSION_MISSING: ${record.logicalPath} is missing XNL metadata apiVersion`);
394
- if (!version) throw new Error(`SKILL_CAPSULE_VERSION_MISSING: ${record.logicalPath} is missing XNL metadata version`);
395
391
  const parsedMetadata = parse(metadataMaterial.content);
396
392
  if (!parsedMetadata || typeof parsedMetadata !== "object" || Array.isArray(parsedMetadata)) throw new Error(`SKILL_CAPSULE_METADATA_INVALID: ${record.logicalPath} Skill metadata must be a YAML object`);
397
393
  const yamlVersion = parsedMetadata.version;
398
- if (yamlVersion !== void 0 && yamlVersion !== version) throw new Error(`SKILL_CAPSULE_VERSION_CONFLICT: ${requiredFqn(record)} YAML version ${JSON.stringify(yamlVersion)} does not match XNL version ${version}`);
394
+ if (typeof yamlVersion !== "string" || !yamlVersion.trim()) throw new Error(`SKILL_CAPSULE_VERSION_MISSING: ${record.logicalPath} Skill metadata must declare a non-empty version`);
395
+ const version = yamlVersion.trim();
399
396
  const hasXnlName = Object.prototype.hasOwnProperty.call(record.node.properties, "name");
400
397
  const xnlName = stringResourceValue(record.node.properties.name);
401
398
  if (hasXnlName && !xnlName) throw new Error(`SKILL_CAPSULE_NAME_INVALID: ${requiredFqn(record)} XNL name must be a non-empty string`);
@@ -404,7 +401,8 @@ async function readSkillCapsule(module, record) {
404
401
  kind: "SkillCapsule",
405
402
  fqn: requiredFqn(record),
406
403
  ...xnlName ? { name: xnlName } : {},
407
- apiVersion,
404
+ envelopeVersion: record.metadata.envelopeVersion,
405
+ specVersion: record.metadata.specVersion,
408
406
  version,
409
407
  description: requiredDescription(record),
410
408
  metadata: {
@@ -1,4 +1,4 @@
1
- import { l as resourceRefToFqn, u as resolveObjectOperationCompilation } from "./src-3pnDAjBD.js";
1
+ import { l as resourceRefToFqn, u as resolveObjectOperationCompilation } from "./src-B0Hja_dk.js";
2
2
  import { c as unsafeUnicodeCodeUnitIssue, i as planResourceMappings, o as safePathLexicalIssue, r as parseResourceMappings, t as ResourceMappingPathError } from "./src-25wZPUyX.js";
3
3
  import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
4
4
  import { basename, dirname, join, posix, resolve } from "node:path";
@@ -70,7 +70,8 @@ async function planSkillCapsuleDistribution(input) {
70
70
  const targetRelativePath = posix.join(identity.name, file.targetRelativePath);
71
71
  return plannedSkillFile({
72
72
  skillFqn: identity.fqn,
73
- skillApiVersion: identity.apiVersion,
73
+ skillEnvelopeVersion: identity.envelopeVersion,
74
+ skillSpecVersion: identity.specVersion,
74
75
  skillVersion: identity.version,
75
76
  capsuleRelativePath: file.targetRelativePath,
76
77
  targetRelativePath
@@ -294,7 +295,8 @@ function skillIdentity(skill) {
294
295
  return Object.freeze({
295
296
  fqn: skill.fqn,
296
297
  name: skillOutputName(skill),
297
- apiVersion: skill.apiVersion,
298
+ envelopeVersion: skill.envelopeVersion,
299
+ specVersion: skill.specVersion,
298
300
  version: skill.version
299
301
  });
300
302
  }
@@ -338,7 +340,7 @@ function assertValidDistributionPlan(plan) {
338
340
  const capsuleByFqn = /* @__PURE__ */ new Map();
339
341
  for (const capsule of plan.capsules) {
340
342
  validateSkillOutputName(capsule.identity.name);
341
- if (!capsule.identity.fqn.trim() || !capsule.identity.apiVersion.trim() || !capsule.identity.version.trim()) failDistribution("SKILL_PLAN_IDENTITY_INVALID", "planned Skill identity requires non-empty FQN, apiVersion and version");
343
+ if (!capsule.identity.fqn.trim() || !capsule.identity.envelopeVersion.trim() || !Number.isSafeInteger(capsule.identity.specVersion) || capsule.identity.specVersion <= 0 || !capsule.identity.version.trim()) failDistribution("SKILL_PLAN_IDENTITY_INVALID", "planned Skill identity requires non-empty FQN/envelopeVersion/version and a positive specVersion");
342
344
  if (capsuleByFqn.has(capsule.identity.fqn)) failDistribution("SKILL_PLAN_CAPSULE_DUPLICATE", `duplicate planned capsule ${capsule.identity.fqn}`);
343
345
  capsuleByFqn.set(capsule.identity.fqn, capsule);
344
346
  for (const file of capsule.files) {
@@ -348,7 +350,7 @@ function assertValidDistributionPlan(plan) {
348
350
  assertCanonicalSkillEntry(capsule);
349
351
  for (const file of capsule.files) {
350
352
  assertValidPlannedFile(file);
351
- if (file.skillFqn !== capsule.identity.fqn || file.skillApiVersion !== capsule.identity.apiVersion || file.skillVersion !== capsule.identity.version) failDistribution("SKILL_PLAN_FILE_OWNER_INVALID", `file ${file.targetRelativePath} has the wrong Skill owner`);
353
+ if (file.skillFqn !== capsule.identity.fqn || file.skillEnvelopeVersion !== capsule.identity.envelopeVersion || file.skillSpecVersion !== capsule.identity.specVersion || file.skillVersion !== capsule.identity.version) failDistribution("SKILL_PLAN_FILE_OWNER_INVALID", `file ${file.targetRelativePath} has the wrong Skill owner`);
352
354
  if (file.targetRelativePath !== posix.join(capsule.identity.name, file.capsuleRelativePath)) failDistribution("SKILL_PLAN_TARGET_INVALID", `file ${file.targetRelativePath} is outside Skill output ${capsule.identity.name}`);
353
355
  }
354
356
  assertCanonicalSkillProvenance(capsule);
@@ -448,7 +450,8 @@ function bytesEqual(left, right) {
448
450
  function fileDigestProjection(file) {
449
451
  return {
450
452
  skillFqn: file.skillFqn,
451
- skillApiVersion: file.skillApiVersion,
453
+ skillEnvelopeVersion: file.skillEnvelopeVersion,
454
+ skillSpecVersion: file.skillSpecVersion,
452
455
  skillVersion: file.skillVersion,
453
456
  capsuleRelativePath: file.capsuleRelativePath,
454
457
  targetRelativePath: file.targetRelativePath,
@@ -476,7 +479,8 @@ function canonicalSkillProvenanceBytes(identity, payloadFiles) {
476
479
  generatedBy: "halfcode.skill-distribution/v1",
477
480
  source: {
478
481
  fqn: identity.fqn,
479
- apiVersion: identity.apiVersion,
482
+ envelopeVersion: identity.envelopeVersion,
483
+ specVersion: identity.specVersion,
480
484
  version: identity.version
481
485
  },
482
486
  payloadFiles: payloadFiles.map((file) => ({
@@ -1,5 +1,5 @@
1
- import "./src-D3qSzGFn.js";
2
- import { t as resolveEffectiveResourceContentIdentities } from "./effective-content-identities-DV5fTr5Q.js";
1
+ import { d as isSpecVersion, u as RESOURCE_ENVELOPE_VERSION } from "./src-jggOQtq3.js";
2
+ import { t as resolveEffectiveResourceContentIdentities } from "./effective-content-identities-CV6XFBZy.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
5
5
  //#region ../runtime-authoring/src/resource-authoring.ts
@@ -37,7 +37,8 @@ function planResourceAuthoring(runtime, input, _config) {
37
37
  if (proposal.operation === "create" !== (proposal.expected.state === "absent")) fail("RESOURCE_AUTHORING_EXPECTED_STATE_INVALID", "Create requires absent state and update requires present state.");
38
38
  const kindDefinition = planning.kindDefinitions.find((item) => item.kind === proposal.kind);
39
39
  if (!kindDefinition) fail("RESOURCE_AUTHORING_KIND_UNKNOWN", `Kind '${proposal.kind}' is not registered.`);
40
- if (!kindDefinition.supportedApiVersions.includes(proposal.apiVersion)) fail("RESOURCE_AUTHORING_API_VERSION_UNSUPPORTED", `Kind '${proposal.kind}' does not support '${proposal.apiVersion}'.`);
40
+ const writerSpecRevision = kindDefinition.specRevisions.find((revision) => revision.specVersion === proposal.writerSpecVersion);
41
+ if (!writerSpecRevision) fail("RESOURCE_AUTHORING_WRITER_SPEC_VERSION_UNREGISTERED", `Kind '${proposal.kind}' has no exact writer spec revision ${proposal.writerSpecVersion}.`);
41
42
  if (!kindDefinition.sourceShapes.includes(proposal.sourceShape)) fail("RESOURCE_AUTHORING_SOURCE_SHAPE_UNSUPPORTED", `Kind '${proposal.kind}' does not support '${proposal.sourceShape}'.`);
42
43
  const catalog = planning.catalogs.find((item) => item.catalogId === proposal.catalogId);
43
44
  if (!catalog) fail("RESOURCE_AUTHORING_CATALOG_UNKNOWN", `Catalog '${proposal.catalogId}' is not registered.`);
@@ -61,7 +62,7 @@ function planResourceAuthoring(runtime, input, _config) {
61
62
  for (const resource of inspection.resources) {
62
63
  if (resourceIds.has(resource.resourceId)) fail("RESOURCE_AUTHORING_AUTHORITY_INVALID", "Authority document repeats a resource identity.");
63
64
  resourceIds.add(resource.resourceId);
64
- if (resource.kind !== proposal.kind || resource.apiVersion !== proposal.apiVersion) fail("RESOURCE_AUTHORING_AUTHORITY_INVALID", "Every authority root must match the proposed Kind and apiVersion.");
65
+ if (resource.kind !== proposal.kind || resource.envelopeVersion !== proposal.envelopeVersion || resource.writerSpecVersion !== proposal.writerSpecVersion) fail("RESOURCE_AUTHORING_AUTHORITY_INVALID", "Every authority root must match the proposed Kind, envelopeVersion and writerSpecVersion.");
65
66
  }
66
67
  if (!resourceIds.has(proposal.resourceId)) fail("RESOURCE_AUTHORING_AUTHORITY_INVALID", `Authority document does not contain target '${proposal.resourceId}'.`);
67
68
  const authorityDigest = digestText(proposal.authorityText);
@@ -70,6 +71,7 @@ function planResourceAuthoring(runtime, input, _config) {
70
71
  schemaVersion: RESOURCE_AUTHORING_SCHEMA_VERSION,
71
72
  proposal,
72
73
  kindDefinition,
74
+ writerSpecRevision,
73
75
  catalog,
74
76
  resources: inspection.resources,
75
77
  authorityDigest
@@ -156,11 +158,12 @@ function verifyCandidate(candidate, prepared, plan) {
156
158
  const identity = identities.get(plan.proposal.resourceId);
157
159
  const resource = target?.resource;
158
160
  const origin = target?.effectiveOrigin;
159
- if (!resource || !identity || !origin || target.kind !== plan.proposal.kind || resource.metadata.apiVersion !== plan.proposal.apiVersion || resource.sourceShape !== plan.proposal.sourceShape || resource.documentUri !== plan.proposal.documentUri || identity.authorityDigest !== plan.authorityDigest || origin.layerId !== candidate.targetLayerId || origin.documentUri !== plan.proposal.documentUri) fail("RESOURCE_AUTHORING_REFRESH_INVALID", "Candidate refresh does not project the planned effective resource and authority.");
161
+ if (!resource || !identity || !origin || target.kind !== plan.proposal.kind || resource.metadata.envelopeVersion !== plan.proposal.envelopeVersion || resource.metadata.specVersion !== plan.proposal.writerSpecVersion || resource.sourceShape !== plan.proposal.sourceShape || resource.documentUri !== plan.proposal.documentUri || identity.authorityDigest !== plan.authorityDigest || origin.layerId !== candidate.targetLayerId || origin.documentUri !== plan.proposal.documentUri) fail("RESOURCE_AUTHORING_REFRESH_INVALID", "Candidate refresh does not project the planned effective resource and authority.");
160
162
  const actualRoots = recordsAtDocument(candidate.tree, plan.proposal.documentUri).map((record) => ({
161
163
  resourceId: record.resourceId,
162
164
  kind: record.kind,
163
- apiVersion: record.metadata.apiVersion
165
+ envelopeVersion: record.metadata.envelopeVersion,
166
+ writerSpecVersion: record.metadata.specVersion
164
167
  })).sort(compareInspectedRoots);
165
168
  const plannedRoots = [...plan.resources].sort(compareInspectedRoots);
166
169
  if (canonicalJson(actualRoots) !== canonicalJson(plannedRoots)) fail("RESOURCE_AUTHORING_REFRESH_INVALID", "Candidate refresh changed the whole authority-document resource set.");
@@ -181,7 +184,8 @@ function verifyCandidate(candidate, prepared, plan) {
181
184
  catalogId: plan.proposal.catalogId,
182
185
  resourceId: plan.proposal.resourceId,
183
186
  kind: plan.proposal.kind,
184
- apiVersion: plan.proposal.apiVersion,
187
+ envelopeVersion: plan.proposal.envelopeVersion,
188
+ writerSpecVersion: plan.proposal.writerSpecVersion,
185
189
  sourceShape: plan.proposal.sourceShape,
186
190
  documentUri: plan.proposal.documentUri,
187
191
  authorityDigestBefore: prepared.authorityDigestBefore,
@@ -206,7 +210,7 @@ function verifyCandidate(candidate, prepared, plan) {
206
210
  function verifyReceipt(value, plan, expected) {
207
211
  const receipt = normalizeReceipt(value);
208
212
  const authorityDigestBefore = plan.proposal.expected.state === "present" ? plan.proposal.expected.authorityDigest : null;
209
- if (receipt.planDigest !== plan.planDigest || receipt.operation !== plan.proposal.operation || receipt.catalogId !== plan.proposal.catalogId || receipt.resourceId !== plan.proposal.resourceId || receipt.kind !== plan.proposal.kind || receipt.apiVersion !== plan.proposal.apiVersion || receipt.sourceShape !== plan.proposal.sourceShape || receipt.documentUri !== plan.proposal.documentUri || receipt.authorityDigestBefore !== authorityDigestBefore || receipt.authorityDigestAfter !== plan.authorityDigest || receipt.registryRevisionBefore !== plan.proposal.expected.registryRevision || receipt.registryRevisionAfter === receipt.registryRevisionBefore || receipt.effectiveOrigin.documentUri !== plan.proposal.documentUri || canonicalJson(receipt.authorityResourceIds) !== canonicalJson(plan.resources.map((item) => item.resourceId).sort(compareCodeUnits)) || expected && canonicalJson(receipt) !== canonicalJson(expected)) fail("RESOURCE_AUTHORING_COMMIT_INVALID", "Durable receipt does not match the exact accepted plan and refresh projection.");
213
+ if (receipt.planDigest !== plan.planDigest || receipt.operation !== plan.proposal.operation || receipt.catalogId !== plan.proposal.catalogId || receipt.resourceId !== plan.proposal.resourceId || receipt.kind !== plan.proposal.kind || receipt.envelopeVersion !== plan.proposal.envelopeVersion || receipt.writerSpecVersion !== plan.proposal.writerSpecVersion || receipt.sourceShape !== plan.proposal.sourceShape || receipt.documentUri !== plan.proposal.documentUri || receipt.authorityDigestBefore !== authorityDigestBefore || receipt.authorityDigestAfter !== plan.authorityDigest || receipt.registryRevisionBefore !== plan.proposal.expected.registryRevision || receipt.registryRevisionAfter === receipt.registryRevisionBefore || receipt.effectiveOrigin.documentUri !== plan.proposal.documentUri || canonicalJson(receipt.authorityResourceIds) !== canonicalJson(plan.resources.map((item) => item.resourceId).sort(compareCodeUnits)) || expected && canonicalJson(receipt) !== canonicalJson(expected)) fail("RESOURCE_AUTHORING_COMMIT_INVALID", "Durable receipt does not match the exact accepted plan and refresh projection.");
210
214
  return receipt;
211
215
  }
212
216
  function normalizeProposal(value) {
@@ -216,13 +220,15 @@ function normalizeProposal(value) {
216
220
  "catalogId",
217
221
  "resourceId",
218
222
  "kind",
219
- "apiVersion",
223
+ "envelopeVersion",
224
+ "writerSpecVersion",
220
225
  "sourceShape",
221
226
  "documentUri",
222
227
  "authorityText",
223
228
  "expected"
224
229
  ], "proposal");
225
230
  if (input.schemaVersion !== "halfcode.resource-authoring/v1") invalid("proposal.schemaVersion");
231
+ if (input.envelopeVersion !== "halfcode.resource-envelope/v1") invalid("proposal.envelopeVersion");
226
232
  if (input.operation !== "create" && input.operation !== "update") invalid("proposal.operation");
227
233
  if (input.sourceShape !== "single-file") fail("RESOURCE_AUTHORING_SOURCE_SHAPE_UNSUPPORTED", "The initial authoring transaction supports only single-file authority.");
228
234
  const documentUri = normalizeDocumentUri(input.documentUri, "proposal.documentUri");
@@ -253,7 +259,8 @@ function normalizeProposal(value) {
253
259
  catalogId: requiredString(input.catalogId, "proposal.catalogId"),
254
260
  resourceId: requiredString(input.resourceId, "proposal.resourceId"),
255
261
  kind: requiredString(input.kind, "proposal.kind"),
256
- apiVersion: requiredString(input.apiVersion, "proposal.apiVersion"),
262
+ envelopeVersion: RESOURCE_ENVELOPE_VERSION,
263
+ writerSpecVersion: exactSpecVersion(input.writerSpecVersion, "proposal.writerSpecVersion"),
257
264
  sourceShape: "single-file",
258
265
  documentUri,
259
266
  authorityText,
@@ -268,20 +275,24 @@ function normalizePlanningAuthority(value) {
268
275
  ], "planning");
269
276
  const registryRevision = digestString(input.registryRevision, "planning.registryRevision");
270
277
  const kindDefinitions = ownArray(input.kindDefinitions, "planning.kindDefinitions").map((item, index) => {
278
+ const location = `planning.kindDefinitions[${index}]`;
271
279
  const record = ownRecord(item, [
272
280
  "kind",
273
- "supportedApiVersions",
281
+ "specRevisions",
274
282
  "sourceShapes",
275
283
  "documentCardinality"
276
- ], `planning.kindDefinitions[${index}]`);
284
+ ], location);
277
285
  const sourceShapes = ownArray(record.sourceShapes, `planning.kindDefinitions[${index}].sourceShapes`).map((shape) => {
278
286
  if (shape !== "single-file") fail("RESOURCE_AUTHORING_SOURCE_SHAPE_UNSUPPORTED", "Planning authority contains an unsupported source shape.");
279
287
  return shape;
280
288
  });
281
289
  if (record.documentCardinality !== "one" && record.documentCardinality !== "many") invalid(`planning.kindDefinitions[${index}].documentCardinality`);
290
+ const specRevisions = ownArray(record.specRevisions, `${location}.specRevisions`).map((revision, revisionIndex) => normalizeSpecRevision(revision, `${location}.specRevisions[${revisionIndex}]`)).sort((left, right) => left.specVersion - right.specVersion);
291
+ if (specRevisions.length === 0) invalid(`${location}.specRevisions`);
292
+ requireUnique(specRevisions.map((revision) => String(revision.specVersion)), `${location} spec revision`);
282
293
  return deepFreeze({
283
- kind: requiredString(record.kind, `planning.kindDefinitions[${index}].kind`),
284
- supportedApiVersions: [...uniqueStrings(record.supportedApiVersions, `planning.kindDefinitions[${index}].supportedApiVersions`)].sort(compareCodeUnits),
294
+ kind: requiredString(record.kind, `${location}.kind`),
295
+ specRevisions,
285
296
  sourceShapes: sourceShapes.sort(compareCodeUnits),
286
297
  documentCardinality: record.documentCardinality
287
298
  });
@@ -317,12 +328,14 @@ function normalizeInspection(value) {
317
328
  const record = ownRecord(item, [
318
329
  "resourceId",
319
330
  "kind",
320
- "apiVersion"
331
+ "envelopeVersion",
332
+ "writerSpecVersion"
321
333
  ], `authorityInspection.resources[${index}]`);
322
334
  return deepFreeze({
323
335
  resourceId: requiredString(record.resourceId, `authorityInspection.resources[${index}].resourceId`),
324
336
  kind: requiredString(record.kind, `authorityInspection.resources[${index}].kind`),
325
- apiVersion: requiredString(record.apiVersion, `authorityInspection.resources[${index}].apiVersion`)
337
+ envelopeVersion: requiredString(record.envelopeVersion, `authorityInspection.resources[${index}].envelopeVersion`),
338
+ writerSpecVersion: exactSpecVersion(record.writerSpecVersion, `authorityInspection.resources[${index}].writerSpecVersion`)
326
339
  });
327
340
  }).sort(compareInspectedRoots);
328
341
  return deepFreeze({
@@ -330,11 +343,40 @@ function normalizeInspection(value) {
330
343
  resources
331
344
  });
332
345
  }
346
+ function normalizeSpecRevision(value, location) {
347
+ const input = ownRecord(value, [
348
+ "specVersion",
349
+ "schemaRef",
350
+ "schemaFingerprint",
351
+ "contractFingerprint",
352
+ "semanticContract",
353
+ "stability"
354
+ ], location);
355
+ const semantic = ownRecord(input.semanticContract, [
356
+ "semanticValidatorFingerprint",
357
+ "referenceProjectionFingerprint",
358
+ "compilerInputFingerprint"
359
+ ], `${location}.semanticContract`);
360
+ if (input.stability !== "experimental" && input.stability !== "stable" && input.stability !== "deprecated") invalid(`${location}.stability`);
361
+ return deepFreeze({
362
+ specVersion: exactSpecVersion(input.specVersion, `${location}.specVersion`),
363
+ schemaRef: requiredString(input.schemaRef, `${location}.schemaRef`),
364
+ schemaFingerprint: digestString(input.schemaFingerprint, `${location}.schemaFingerprint`),
365
+ contractFingerprint: digestString(input.contractFingerprint, `${location}.contractFingerprint`),
366
+ semanticContract: {
367
+ semanticValidatorFingerprint: digestString(semantic.semanticValidatorFingerprint, `${location}.semanticContract.semanticValidatorFingerprint`),
368
+ referenceProjectionFingerprint: digestString(semantic.referenceProjectionFingerprint, `${location}.semanticContract.referenceProjectionFingerprint`),
369
+ compilerInputFingerprint: digestString(semantic.compilerInputFingerprint, `${location}.semanticContract.compilerInputFingerprint`)
370
+ },
371
+ stability: input.stability
372
+ });
373
+ }
333
374
  function normalizePlan(value) {
334
375
  const input = ownRecord(value, [
335
376
  "schemaVersion",
336
377
  "proposal",
337
378
  "kindDefinition",
379
+ "writerSpecRevision",
338
380
  "catalog",
339
381
  "resources",
340
382
  "authorityDigest",
@@ -351,6 +393,7 @@ function normalizePlan(value) {
351
393
  schemaVersion: RESOURCE_AUTHORING_SCHEMA_VERSION,
352
394
  proposal,
353
395
  kindDefinition: authority.kindDefinitions[0],
396
+ writerSpecRevision: normalizeSpecRevision(input.writerSpecRevision, "plan.writerSpecRevision"),
354
397
  catalog: authority.catalogs[0],
355
398
  resources: normalizeInspection({
356
399
  documentUri: proposal.documentUri,
@@ -358,6 +401,8 @@ function normalizePlan(value) {
358
401
  }).resources,
359
402
  authorityDigest: digestString(input.authorityDigest, "plan.authorityDigest")
360
403
  };
404
+ const admittedRevision = unsigned.kindDefinition.specRevisions.find((revision) => revision.specVersion === proposal.writerSpecVersion);
405
+ if (!admittedRevision || canonicalJson(admittedRevision) !== canonicalJson(unsigned.writerSpecRevision)) fail("RESOURCE_AUTHORING_PLAN_FORGED", "Plan writer spec revision is not the exact admitted revision.");
361
406
  const planDigest = digestString(input.planDigest, "plan.planDigest");
362
407
  if (digestValue(unsigned) !== planDigest) fail("RESOURCE_AUTHORING_PLAN_FORGED", "Resource authoring plan digest is invalid.");
363
408
  return deepFreeze({
@@ -388,7 +433,8 @@ function normalizeReceipt(value) {
388
433
  "catalogId",
389
434
  "resourceId",
390
435
  "kind",
391
- "apiVersion",
436
+ "envelopeVersion",
437
+ "writerSpecVersion",
392
438
  "sourceShape",
393
439
  "documentUri",
394
440
  "authorityDigestBefore",
@@ -401,6 +447,7 @@ function normalizeReceipt(value) {
401
447
  "receiptDigest"
402
448
  ], "receipt");
403
449
  if (input.schemaVersion !== "halfcode.resource-authoring/v1") invalid("receipt.schemaVersion");
450
+ if (input.envelopeVersion !== "halfcode.resource-envelope/v1") invalid("receipt.envelopeVersion");
404
451
  if (input.operation !== "create" && input.operation !== "update") invalid("receipt.operation");
405
452
  if (input.sourceShape !== "single-file") invalid("receipt.sourceShape");
406
453
  const origin = ownRecordOptional(input.effectiveOrigin, [
@@ -418,7 +465,8 @@ function normalizeReceipt(value) {
418
465
  catalogId: requiredString(input.catalogId, "receipt.catalogId"),
419
466
  resourceId: requiredString(input.resourceId, "receipt.resourceId"),
420
467
  kind: requiredString(input.kind, "receipt.kind"),
421
- apiVersion: requiredString(input.apiVersion, "receipt.apiVersion"),
468
+ envelopeVersion: RESOURCE_ENVELOPE_VERSION,
469
+ writerSpecVersion: exactSpecVersion(input.writerSpecVersion, "receipt.writerSpecVersion"),
422
470
  sourceShape: "single-file",
423
471
  documentUri: normalizeDocumentUri(input.documentUri, "receipt.documentUri"),
424
472
  authorityDigestBefore: input.authorityDigestBefore === null ? null : digestString(input.authorityDigestBefore, "receipt.authorityDigestBefore"),
@@ -451,7 +499,7 @@ function recordsAtDocument(tree, documentUri) {
451
499
  return [...tree.registry.byKind.values()].flatMap((records) => [...records]).filter((record) => record.documentUri === documentUri);
452
500
  }
453
501
  function compareInspectedRoots(left, right) {
454
- return compareCodeUnits(left.resourceId, right.resourceId) || compareCodeUnits(left.kind, right.kind) || compareCodeUnits(left.apiVersion, right.apiVersion);
502
+ return compareCodeUnits(left.resourceId, right.resourceId) || compareCodeUnits(left.kind, right.kind) || compareCodeUnits(left.envelopeVersion, right.envelopeVersion) || left.writerSpecVersion - right.writerSpecVersion;
455
503
  }
456
504
  function compareCodeUnits(left, right) {
457
505
  return left < right ? -1 : left > right ? 1 : 0;
@@ -508,6 +556,10 @@ function digestString(value, location) {
508
556
  if (!/^sha256:[0-9a-f]{64}$/u.test(text)) invalid(location);
509
557
  return text;
510
558
  }
559
+ function exactSpecVersion(value, location) {
560
+ if (!isSpecVersion(value)) invalid(location);
561
+ return value;
562
+ }
511
563
  function normalizeDocumentUri(value, location) {
512
564
  const text = requiredString(value, location);
513
565
  const logicalPath = text.startsWith("vfs://@/") ? text.slice(8) : "";