halfcode-compiler.xnl 0.2.9 → 0.3.1

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 (60) hide show
  1. package/README.md +5 -3
  2. package/dist/application-assembly.d.ts +3 -2
  3. package/dist/application-assembly.js +2 -2
  4. package/dist/authoring-runtime.d.ts +30 -9
  5. package/dist/authoring-runtime.js +2 -2
  6. package/dist/code-execution-closure-CXORuheJ.d.ts +59 -0
  7. package/dist/{effective-content-identities-DV5fTr5Q.js → effective-content-identities-CV6XFBZy.js} +8 -8
  8. package/dist/{effective-content-identities-CSVFi-N5.d.ts → effective-content-identities-Cf0ygCXk.d.ts} +2 -2
  9. package/dist/{index-B515mMkO.d.ts → index-B9Pp3Zgj.d.ts} +10 -6
  10. package/dist/index-By8x4reS.d.ts +443 -0
  11. package/dist/{index-CF4nEkOi.d.ts → index-Csi_AIoU.d.ts} +1 -1
  12. package/dist/{index-Cqh9pEBa.d.ts → index-DEikbOFh.d.ts} +2 -1
  13. package/dist/index.d.ts +9 -6
  14. package/dist/index.js +6 -5
  15. package/dist/kind-definition.d.ts +118 -10
  16. package/dist/kind-definition.js +826 -15
  17. package/dist/resource-core-CoTu6FNS.d.ts +48 -0
  18. package/dist/resource-core.d.ts +5 -4
  19. package/dist/resource-core.js +25 -11
  20. package/dist/resource-package-read-port-BLBb4cz5.d.ts +33 -0
  21. package/dist/resource-projection.d.ts +1 -1
  22. package/dist/skill-capsule.d.ts +2 -2
  23. package/dist/skill-capsule.js +1 -1
  24. package/dist/{src-3pnDAjBD.js → src-BX7wrSq1.js} +330 -8
  25. package/dist/{src-3MZLm339.js → src-MPJZB76z.js} +122 -20
  26. package/dist/{src-CaQ25rMj.js → src-QI7NRyC8.js} +11 -7
  27. package/dist/{src-D3qSzGFn.js → src-jggOQtq3.js} +511 -113
  28. package/dist/system-skills/resource-dsl/KindDefinitions/SkillCapsule/manifest.xnl +15 -3
  29. package/dist/system-skills/resource-dsl/KindDefinitions/WikiPage/manifest.xnl +15 -3
  30. package/dist/system-skills/resource-dsl/SkillCapsules/System/SKILL.metadata.yaml +1 -0
  31. package/dist/system-skills/resource-dsl/SkillCapsules/System/manifest.xnl +1 -1
  32. package/dist/system-skills/resource-dsl/SkillCapsules/System/resource-mappings.xnl +1 -1
  33. package/dist/system-skills/resource-dsl/WikiPages/documents/manifest.xnl +1 -1
  34. package/dist/system-skills/resource-dsl/WikiPages/files/manifest.xnl +1 -1
  35. package/dist/system-skills/resource-dsl/WikiPages/index/manifest.xnl +1 -1
  36. package/dist/system-skills/resource-dsl/WikiPages/language/manifest.xnl +1 -1
  37. package/dist/system-skills/resource-dsl/WikiPages/migration/manifest.xnl +1 -1
  38. package/dist/system-skills/resource-dsl/WikiPages/projections/manifest.xnl +1 -1
  39. package/dist/system-skills/resource-dsl/WikiPages/skill-capsules/manifest.xnl +1 -1
  40. package/dist/system-skills/resource-dsl/documents.md +33 -17
  41. package/dist/system-skills/resource-dsl/examples/function.xnl +1 -1
  42. package/dist/system-skills/resource-dsl/examples/kind-definition.xnl +15 -3
  43. package/dist/system-skills/resource-dsl/examples/resource-mappings.xnl +1 -1
  44. package/dist/system-skills/resource-dsl/examples/resource-package.xnl +1 -1
  45. package/dist/system-skills/resource-dsl/examples/skill-capsule-authoring.xnl +1 -1
  46. package/dist/system-skills/resource-dsl/examples/skill-capsule-devops.xnl +1 -1
  47. package/dist/system-skills/resource-dsl/examples/skill-capsule-kind-definition.xnl +14 -3
  48. package/dist/system-skills/resource-dsl/examples/skill-capsule-resource-dsl.xnl +1 -1
  49. package/dist/system-skills/resource-dsl/examples/skill-capsule-run.xnl +1 -1
  50. package/dist/system-skills/resource-dsl/index.md +5 -5
  51. package/dist/system-skills/resource-dsl/language.md +10 -8
  52. package/dist/system-skills/resource-dsl/manifest.xnl +1 -1
  53. package/dist/system-skills/resource-dsl/migration.md +6 -3
  54. package/dist/system-skills/resource-dsl/projections.md +7 -6
  55. package/dist/system-skills/resource-dsl/skill-capsules.md +8 -7
  56. package/dist/system-skills/sys-halfcode-resource-dsl.plan.js +84 -59
  57. package/dist/testing.js +1 -1
  58. package/package.json +1 -1
  59. package/dist/index-CDIT65pI.d.ts +0 -192
  60. package/dist/resource-core-C0Fj-VJA.d.ts +0 -29
@@ -1,5 +1,6 @@
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
+ import { d as CodeClosureError, p as validateCodeClosure } from "./src-BX7wrSq1.js";
3
4
  import { createHash } from "node:crypto";
4
5
  import { AsyncLocalStorage } from "node:async_hooks";
5
6
  //#region ../runtime-authoring/src/resource-authoring.ts
@@ -37,7 +38,8 @@ function planResourceAuthoring(runtime, input, _config) {
37
38
  if (proposal.operation === "create" !== (proposal.expected.state === "absent")) fail("RESOURCE_AUTHORING_EXPECTED_STATE_INVALID", "Create requires absent state and update requires present state.");
38
39
  const kindDefinition = planning.kindDefinitions.find((item) => item.kind === proposal.kind);
39
40
  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}'.`);
41
+ const writerSpecRevision = kindDefinition.specRevisions.find((revision) => revision.specVersion === proposal.writerSpecVersion);
42
+ if (!writerSpecRevision) fail("RESOURCE_AUTHORING_WRITER_SPEC_VERSION_UNREGISTERED", `Kind '${proposal.kind}' has no exact writer spec revision ${proposal.writerSpecVersion}.`);
41
43
  if (!kindDefinition.sourceShapes.includes(proposal.sourceShape)) fail("RESOURCE_AUTHORING_SOURCE_SHAPE_UNSUPPORTED", `Kind '${proposal.kind}' does not support '${proposal.sourceShape}'.`);
42
44
  const catalog = planning.catalogs.find((item) => item.catalogId === proposal.catalogId);
43
45
  if (!catalog) fail("RESOURCE_AUTHORING_CATALOG_UNKNOWN", `Catalog '${proposal.catalogId}' is not registered.`);
@@ -61,7 +63,7 @@ function planResourceAuthoring(runtime, input, _config) {
61
63
  for (const resource of inspection.resources) {
62
64
  if (resourceIds.has(resource.resourceId)) fail("RESOURCE_AUTHORING_AUTHORITY_INVALID", "Authority document repeats a resource identity.");
63
65
  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.");
66
+ 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
67
  }
66
68
  if (!resourceIds.has(proposal.resourceId)) fail("RESOURCE_AUTHORING_AUTHORITY_INVALID", `Authority document does not contain target '${proposal.resourceId}'.`);
67
69
  const authorityDigest = digestText(proposal.authorityText);
@@ -70,6 +72,7 @@ function planResourceAuthoring(runtime, input, _config) {
70
72
  schemaVersion: RESOURCE_AUTHORING_SCHEMA_VERSION,
71
73
  proposal,
72
74
  kindDefinition,
75
+ writerSpecRevision,
73
76
  catalog,
74
77
  resources: inspection.resources,
75
78
  authorityDigest
@@ -156,11 +159,12 @@ function verifyCandidate(candidate, prepared, plan) {
156
159
  const identity = identities.get(plan.proposal.resourceId);
157
160
  const resource = target?.resource;
158
161
  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.");
162
+ 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
163
  const actualRoots = recordsAtDocument(candidate.tree, plan.proposal.documentUri).map((record) => ({
161
164
  resourceId: record.resourceId,
162
165
  kind: record.kind,
163
- apiVersion: record.metadata.apiVersion
166
+ envelopeVersion: record.metadata.envelopeVersion,
167
+ writerSpecVersion: record.metadata.specVersion
164
168
  })).sort(compareInspectedRoots);
165
169
  const plannedRoots = [...plan.resources].sort(compareInspectedRoots);
166
170
  if (canonicalJson(actualRoots) !== canonicalJson(plannedRoots)) fail("RESOURCE_AUTHORING_REFRESH_INVALID", "Candidate refresh changed the whole authority-document resource set.");
@@ -181,7 +185,8 @@ function verifyCandidate(candidate, prepared, plan) {
181
185
  catalogId: plan.proposal.catalogId,
182
186
  resourceId: plan.proposal.resourceId,
183
187
  kind: plan.proposal.kind,
184
- apiVersion: plan.proposal.apiVersion,
188
+ envelopeVersion: plan.proposal.envelopeVersion,
189
+ writerSpecVersion: plan.proposal.writerSpecVersion,
185
190
  sourceShape: plan.proposal.sourceShape,
186
191
  documentUri: plan.proposal.documentUri,
187
192
  authorityDigestBefore: prepared.authorityDigestBefore,
@@ -206,7 +211,7 @@ function verifyCandidate(candidate, prepared, plan) {
206
211
  function verifyReceipt(value, plan, expected) {
207
212
  const receipt = normalizeReceipt(value);
208
213
  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.");
214
+ 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
215
  return receipt;
211
216
  }
212
217
  function normalizeProposal(value) {
@@ -216,13 +221,15 @@ function normalizeProposal(value) {
216
221
  "catalogId",
217
222
  "resourceId",
218
223
  "kind",
219
- "apiVersion",
224
+ "envelopeVersion",
225
+ "writerSpecVersion",
220
226
  "sourceShape",
221
227
  "documentUri",
222
228
  "authorityText",
223
229
  "expected"
224
230
  ], "proposal");
225
231
  if (input.schemaVersion !== "halfcode.resource-authoring/v1") invalid("proposal.schemaVersion");
232
+ if (input.envelopeVersion !== "halfcode.resource-envelope/v1") invalid("proposal.envelopeVersion");
226
233
  if (input.operation !== "create" && input.operation !== "update") invalid("proposal.operation");
227
234
  if (input.sourceShape !== "single-file") fail("RESOURCE_AUTHORING_SOURCE_SHAPE_UNSUPPORTED", "The initial authoring transaction supports only single-file authority.");
228
235
  const documentUri = normalizeDocumentUri(input.documentUri, "proposal.documentUri");
@@ -253,7 +260,8 @@ function normalizeProposal(value) {
253
260
  catalogId: requiredString(input.catalogId, "proposal.catalogId"),
254
261
  resourceId: requiredString(input.resourceId, "proposal.resourceId"),
255
262
  kind: requiredString(input.kind, "proposal.kind"),
256
- apiVersion: requiredString(input.apiVersion, "proposal.apiVersion"),
263
+ envelopeVersion: RESOURCE_ENVELOPE_VERSION,
264
+ writerSpecVersion: exactSpecVersion(input.writerSpecVersion, "proposal.writerSpecVersion"),
257
265
  sourceShape: "single-file",
258
266
  documentUri,
259
267
  authorityText,
@@ -268,20 +276,24 @@ function normalizePlanningAuthority(value) {
268
276
  ], "planning");
269
277
  const registryRevision = digestString(input.registryRevision, "planning.registryRevision");
270
278
  const kindDefinitions = ownArray(input.kindDefinitions, "planning.kindDefinitions").map((item, index) => {
279
+ const location = `planning.kindDefinitions[${index}]`;
271
280
  const record = ownRecord(item, [
272
281
  "kind",
273
- "supportedApiVersions",
282
+ "specRevisions",
274
283
  "sourceShapes",
275
284
  "documentCardinality"
276
- ], `planning.kindDefinitions[${index}]`);
285
+ ], location);
277
286
  const sourceShapes = ownArray(record.sourceShapes, `planning.kindDefinitions[${index}].sourceShapes`).map((shape) => {
278
287
  if (shape !== "single-file") fail("RESOURCE_AUTHORING_SOURCE_SHAPE_UNSUPPORTED", "Planning authority contains an unsupported source shape.");
279
288
  return shape;
280
289
  });
281
290
  if (record.documentCardinality !== "one" && record.documentCardinality !== "many") invalid(`planning.kindDefinitions[${index}].documentCardinality`);
291
+ const specRevisions = ownArray(record.specRevisions, `${location}.specRevisions`).map((revision, revisionIndex) => normalizeSpecRevision(revision, `${location}.specRevisions[${revisionIndex}]`)).sort((left, right) => left.specVersion - right.specVersion);
292
+ if (specRevisions.length === 0) invalid(`${location}.specRevisions`);
293
+ requireUnique(specRevisions.map((revision) => String(revision.specVersion)), `${location} spec revision`);
282
294
  return deepFreeze({
283
- kind: requiredString(record.kind, `planning.kindDefinitions[${index}].kind`),
284
- supportedApiVersions: [...uniqueStrings(record.supportedApiVersions, `planning.kindDefinitions[${index}].supportedApiVersions`)].sort(compareCodeUnits),
295
+ kind: requiredString(record.kind, `${location}.kind`),
296
+ specRevisions,
285
297
  sourceShapes: sourceShapes.sort(compareCodeUnits),
286
298
  documentCardinality: record.documentCardinality
287
299
  });
@@ -317,12 +329,14 @@ function normalizeInspection(value) {
317
329
  const record = ownRecord(item, [
318
330
  "resourceId",
319
331
  "kind",
320
- "apiVersion"
332
+ "envelopeVersion",
333
+ "writerSpecVersion"
321
334
  ], `authorityInspection.resources[${index}]`);
322
335
  return deepFreeze({
323
336
  resourceId: requiredString(record.resourceId, `authorityInspection.resources[${index}].resourceId`),
324
337
  kind: requiredString(record.kind, `authorityInspection.resources[${index}].kind`),
325
- apiVersion: requiredString(record.apiVersion, `authorityInspection.resources[${index}].apiVersion`)
338
+ envelopeVersion: requiredString(record.envelopeVersion, `authorityInspection.resources[${index}].envelopeVersion`),
339
+ writerSpecVersion: exactSpecVersion(record.writerSpecVersion, `authorityInspection.resources[${index}].writerSpecVersion`)
326
340
  });
327
341
  }).sort(compareInspectedRoots);
328
342
  return deepFreeze({
@@ -330,11 +344,40 @@ function normalizeInspection(value) {
330
344
  resources
331
345
  });
332
346
  }
347
+ function normalizeSpecRevision(value, location) {
348
+ const input = ownRecord(value, [
349
+ "specVersion",
350
+ "schemaRef",
351
+ "schemaFingerprint",
352
+ "contractFingerprint",
353
+ "semanticContract",
354
+ "stability"
355
+ ], location);
356
+ const semantic = ownRecord(input.semanticContract, [
357
+ "semanticValidatorFingerprint",
358
+ "referenceProjectionFingerprint",
359
+ "compilerInputFingerprint"
360
+ ], `${location}.semanticContract`);
361
+ if (input.stability !== "experimental" && input.stability !== "stable" && input.stability !== "deprecated") invalid(`${location}.stability`);
362
+ return deepFreeze({
363
+ specVersion: exactSpecVersion(input.specVersion, `${location}.specVersion`),
364
+ schemaRef: requiredString(input.schemaRef, `${location}.schemaRef`),
365
+ schemaFingerprint: digestString(input.schemaFingerprint, `${location}.schemaFingerprint`),
366
+ contractFingerprint: digestString(input.contractFingerprint, `${location}.contractFingerprint`),
367
+ semanticContract: {
368
+ semanticValidatorFingerprint: digestString(semantic.semanticValidatorFingerprint, `${location}.semanticContract.semanticValidatorFingerprint`),
369
+ referenceProjectionFingerprint: digestString(semantic.referenceProjectionFingerprint, `${location}.semanticContract.referenceProjectionFingerprint`),
370
+ compilerInputFingerprint: digestString(semantic.compilerInputFingerprint, `${location}.semanticContract.compilerInputFingerprint`)
371
+ },
372
+ stability: input.stability
373
+ });
374
+ }
333
375
  function normalizePlan(value) {
334
376
  const input = ownRecord(value, [
335
377
  "schemaVersion",
336
378
  "proposal",
337
379
  "kindDefinition",
380
+ "writerSpecRevision",
338
381
  "catalog",
339
382
  "resources",
340
383
  "authorityDigest",
@@ -351,6 +394,7 @@ function normalizePlan(value) {
351
394
  schemaVersion: RESOURCE_AUTHORING_SCHEMA_VERSION,
352
395
  proposal,
353
396
  kindDefinition: authority.kindDefinitions[0],
397
+ writerSpecRevision: normalizeSpecRevision(input.writerSpecRevision, "plan.writerSpecRevision"),
354
398
  catalog: authority.catalogs[0],
355
399
  resources: normalizeInspection({
356
400
  documentUri: proposal.documentUri,
@@ -358,6 +402,8 @@ function normalizePlan(value) {
358
402
  }).resources,
359
403
  authorityDigest: digestString(input.authorityDigest, "plan.authorityDigest")
360
404
  };
405
+ const admittedRevision = unsigned.kindDefinition.specRevisions.find((revision) => revision.specVersion === proposal.writerSpecVersion);
406
+ if (!admittedRevision || canonicalJson(admittedRevision) !== canonicalJson(unsigned.writerSpecRevision)) fail("RESOURCE_AUTHORING_PLAN_FORGED", "Plan writer spec revision is not the exact admitted revision.");
361
407
  const planDigest = digestString(input.planDigest, "plan.planDigest");
362
408
  if (digestValue(unsigned) !== planDigest) fail("RESOURCE_AUTHORING_PLAN_FORGED", "Resource authoring plan digest is invalid.");
363
409
  return deepFreeze({
@@ -388,7 +434,8 @@ function normalizeReceipt(value) {
388
434
  "catalogId",
389
435
  "resourceId",
390
436
  "kind",
391
- "apiVersion",
437
+ "envelopeVersion",
438
+ "writerSpecVersion",
392
439
  "sourceShape",
393
440
  "documentUri",
394
441
  "authorityDigestBefore",
@@ -401,6 +448,7 @@ function normalizeReceipt(value) {
401
448
  "receiptDigest"
402
449
  ], "receipt");
403
450
  if (input.schemaVersion !== "halfcode.resource-authoring/v1") invalid("receipt.schemaVersion");
451
+ if (input.envelopeVersion !== "halfcode.resource-envelope/v1") invalid("receipt.envelopeVersion");
404
452
  if (input.operation !== "create" && input.operation !== "update") invalid("receipt.operation");
405
453
  if (input.sourceShape !== "single-file") invalid("receipt.sourceShape");
406
454
  const origin = ownRecordOptional(input.effectiveOrigin, [
@@ -418,7 +466,8 @@ function normalizeReceipt(value) {
418
466
  catalogId: requiredString(input.catalogId, "receipt.catalogId"),
419
467
  resourceId: requiredString(input.resourceId, "receipt.resourceId"),
420
468
  kind: requiredString(input.kind, "receipt.kind"),
421
- apiVersion: requiredString(input.apiVersion, "receipt.apiVersion"),
469
+ envelopeVersion: RESOURCE_ENVELOPE_VERSION,
470
+ writerSpecVersion: exactSpecVersion(input.writerSpecVersion, "receipt.writerSpecVersion"),
422
471
  sourceShape: "single-file",
423
472
  documentUri: normalizeDocumentUri(input.documentUri, "receipt.documentUri"),
424
473
  authorityDigestBefore: input.authorityDigestBefore === null ? null : digestString(input.authorityDigestBefore, "receipt.authorityDigestBefore"),
@@ -451,7 +500,7 @@ function recordsAtDocument(tree, documentUri) {
451
500
  return [...tree.registry.byKind.values()].flatMap((records) => [...records]).filter((record) => record.documentUri === documentUri);
452
501
  }
453
502
  function compareInspectedRoots(left, right) {
454
- return compareCodeUnits(left.resourceId, right.resourceId) || compareCodeUnits(left.kind, right.kind) || compareCodeUnits(left.apiVersion, right.apiVersion);
503
+ return compareCodeUnits(left.resourceId, right.resourceId) || compareCodeUnits(left.kind, right.kind) || compareCodeUnits(left.envelopeVersion, right.envelopeVersion) || left.writerSpecVersion - right.writerSpecVersion;
455
504
  }
456
505
  function compareCodeUnits(left, right) {
457
506
  return left < right ? -1 : left > right ? 1 : 0;
@@ -508,6 +557,10 @@ function digestString(value, location) {
508
557
  if (!/^sha256:[0-9a-f]{64}$/u.test(text)) invalid(location);
509
558
  return text;
510
559
  }
560
+ function exactSpecVersion(value, location) {
561
+ if (!isSpecVersion(value)) invalid(location);
562
+ return value;
563
+ }
511
564
  function normalizeDocumentUri(value, location) {
512
565
  const text = requiredString(value, location);
513
566
  const logicalPath = text.startsWith("vfs://@/") ? text.slice(8) : "";
@@ -548,6 +601,55 @@ function fail(code, message) {
548
601
  throw new ResourceAuthoringError(code, message);
549
602
  }
550
603
  //#endregion
604
+ //#region ../runtime-authoring/src/code-execution.ts
605
+ /** Each restore owns a fresh module table; this is not an untrusted-code sandbox. */
606
+ function restoreCodeClosure(runtime, input, expectedDigest) {
607
+ validateCodeClosure(input, runtime.environment, expectedDigest);
608
+ const closure = JSON.parse(JSON.stringify(input));
609
+ const ambient = /* @__PURE__ */ new Map();
610
+ for (const name of Object.keys(closure.environment.ambient)) {
611
+ if (!runtime.ambientModules || !Object.hasOwn(runtime.ambientModules, name)) throw new CodeClosureError("missing-ambient", `Missing explicit ambient exports: ${name}`);
612
+ ambient.set(name, runtime.ambientModules[name]);
613
+ }
614
+ const modules = new Map(closure.modules.map((module) => [module.path, module]));
615
+ const assets = new Map(closure.assets.map((asset) => [asset.path, asset.format === "json" ? JSON.parse(asset.content) : asset.content]));
616
+ const table = /* @__PURE__ */ new Map();
617
+ const load = (path) => {
618
+ const cached = table.get(path);
619
+ if (cached) return cached.exports;
620
+ const source = modules.get(path);
621
+ if (!source) throw new CodeClosureError("missing-module", `Frozen module absent: ${path}`);
622
+ const module = { exports: {} };
623
+ table.set(path, module);
624
+ const edges = new Map(source.dependencies.map((edge) => [edge.specifier, edge]));
625
+ const requireFrozen = (specifier) => {
626
+ const edge = edges.get(specifier);
627
+ if (!edge) throw new CodeClosureError("undeclared-dependency", `No frozen edge for ${path}: ${specifier}`);
628
+ if (edge.kind === "module") return load(edge.target);
629
+ if (edge.kind === "asset") return assets.get(edge.target);
630
+ return ambient.get(edge.target);
631
+ };
632
+ try {
633
+ new Function("require", "module", "exports", source.code)(requireFrozen, module, module.exports);
634
+ return module.exports;
635
+ } catch (error) {
636
+ table.delete(path);
637
+ throw error;
638
+ }
639
+ };
640
+ return Object.freeze({
641
+ binding: Object.freeze(closure.binding),
642
+ closureDigest: closure.digest,
643
+ exports: load(closure.entryPath)
644
+ });
645
+ }
646
+ function bindCodeClosure(runtime, closure, expectedDigest) {
647
+ const restored = restoreCodeClosure(runtime, closure, expectedDigest);
648
+ const callable = restored.exports[restored.binding.exportName];
649
+ if (typeof callable !== "function") throw new CodeClosureError("missing-export", `CodeBinding export is not callable: ${restored.binding.exportName}`);
650
+ return (...args) => Reflect.apply(callable, void 0, args);
651
+ }
652
+ //#endregion
551
653
  //#region ../runtime-authoring/src/index.ts
552
654
  const runtimeScope = new AsyncLocalStorage();
553
655
  function runWithRuntime(runtime, callback) {
@@ -585,4 +687,4 @@ const runtimeAuthoringPackage = {
585
687
  owns: "target-neutral deterministic-code runtime and effect boundary"
586
688
  };
587
689
  //#endregion
588
- export { runWithRuntime as a, ResourceAuthoringError as c, planResourceAuthoring as d, logger as i, applyResourceAuthoring as l, currentRuntime as n, runtimeAuthoringPackage as o, invokeEffect as r, RESOURCE_AUTHORING_SCHEMA_VERSION as s, createAuthoringBindings as t, deriveResourceAuthoringRegistryRevision as u };
690
+ export { runWithRuntime as a, restoreCodeClosure as c, applyResourceAuthoring as d, deriveResourceAuthoringRegistryRevision as f, logger as i, RESOURCE_AUTHORING_SCHEMA_VERSION as l, currentRuntime as n, runtimeAuthoringPackage as o, planResourceAuthoring as p, invokeEffect as r, bindCodeClosure as s, createAuthoringBindings as t, ResourceAuthoringError as u };
@@ -1,4 +1,4 @@
1
- import { l as resourceRefToFqn, u as resolveObjectOperationCompilation } from "./src-3pnDAjBD.js";
1
+ import { l as resourceRefToFqn, m as resolveObjectOperationCompilation } from "./src-BX7wrSq1.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) => ({