@hardkas/artifacts 0.8.2-alpha → 0.8.4-alpha

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 (3) hide show
  1. package/dist/index.d.ts +628 -33
  2. package/dist/index.js +497 -60
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@hardkas/artifacts",
4
- version: "0.8.1-alpha",
4
+ version: "0.8.4-alpha",
5
5
  type: "module",
6
6
  license: "MIT",
7
7
  author: "Javier Rodriguez",
@@ -53,7 +53,11 @@ var ARTIFACT_SCHEMAS = {
53
53
  SNAPSHOT: "hardkas.snapshot",
54
54
  IGRA_TX_PLAN: "hardkas.igraTxPlan.v1",
55
55
  IGRA_SIGNED_TX: "hardkas.igraSignedTx.v1",
56
- IGRA_TX_RECEIPT: "hardkas.igraTxReceipt.v1"
56
+ IGRA_TX_RECEIPT: "hardkas.igraTxReceipt.v1",
57
+ POLICY: "hardkas.policy.v1",
58
+ NETWORK_PROFILE: "hardkas.networkProfile.v1",
59
+ ASSUMPTION: "hardkas.assumption.v1",
60
+ MIGRATION_RECEIPT: "hardkas.migrationReceipt.v1"
57
61
  };
58
62
 
59
63
  // src/igra-artifacts.ts
@@ -345,6 +349,41 @@ var AccountRefSchema = z.object({
345
349
  accountName: z.string().optional(),
346
350
  input: z.string().optional()
347
351
  });
352
+ var PolicySchema = BaseArtifactSchema.extend({
353
+ schema: z.literal("hardkas.policy.v1"),
354
+ decision: z.enum(["ALLOW", "DENY"]),
355
+ rules: z.array(
356
+ z.object({
357
+ id: z.string(),
358
+ result: z.enum(["PASS", "FAIL"]),
359
+ inputHash: z.string().optional()
360
+ })
361
+ )
362
+ });
363
+ var NetworkProfileSchema = BaseArtifactSchema.extend({
364
+ schema: z.literal("hardkas.networkProfile.v1"),
365
+ networkProfileId: z.string(),
366
+ layer: z.string(),
367
+ capabilities: z.record(z.any())
368
+ });
369
+ var AssumptionSchema = BaseArtifactSchema.extend({
370
+ schema: z.literal("hardkas.assumption.v1"),
371
+ settlement: z.string().optional(),
372
+ securityModel: z.string().optional(),
373
+ bridgePhase: z.string().optional(),
374
+ exitModel: z.string().optional(),
375
+ customAssumptions: z.record(z.any()).optional()
376
+ });
377
+ var MigrationReceiptSchema = BaseArtifactSchema.extend({
378
+ schema: z.literal("hardkas.migrationReceipt.v1"),
379
+ oldHash: z.string(),
380
+ newHash: z.string(),
381
+ fromSchema: z.string(),
382
+ toSchema: z.string(),
383
+ migrationId: z.string(),
384
+ migrationVersion: z.string().optional(),
385
+ decision: z.literal("MIGRATED_WITH_PROOF")
386
+ });
348
387
  var TxPlanSchema = BaseArtifactSchema.extend({
349
388
  schema: z.literal("hardkas.txPlan"),
350
389
  networkId: kaspaNetworkIdSchema,
@@ -374,7 +413,11 @@ var TxPlanSchema = BaseArtifactSchema.extend({
374
413
  address: z.string(),
375
414
  amountSompi: z.string()
376
415
  }).optional(),
377
- rpcUrl: z.string().optional()
416
+ rpcUrl: z.string().optional(),
417
+ networkProfileRef: z.string().optional(),
418
+ policyRef: z.string().optional(),
419
+ policyRefs: z.array(z.string()).optional(),
420
+ assumptionRef: z.string().optional()
378
421
  });
379
422
  var DagContextSchema = z.object({
380
423
  mode: z.enum(["linear", "dag-light"]),
@@ -539,6 +582,7 @@ var RuntimeSessionSchema = BaseArtifactSchema.extend({
539
582
 
540
583
  // src/verify.ts
541
584
  import fs from "fs";
585
+ import path from "path";
542
586
 
543
587
  // src/feeVerify.ts
544
588
  import {
@@ -727,12 +771,21 @@ function verifyLineage(artifact, parent, options = {}) {
727
771
  }
728
772
  if (parent) {
729
773
  const validTransitions = {
730
- "hardkas.snapshot": ["hardkas.txPlan"],
731
- "hardkas.txPlan": ["hardkas.signedTx"],
732
- "hardkas.signedTx": ["hardkas.txReceipt"]
774
+ "hardkas.snapshot": ["hardkas.txPlan", "hardkas.migrationReceipt.v1"],
775
+ "hardkas.txPlan": ["hardkas.signedTx", "hardkas.migrationReceipt.v1"],
776
+ "hardkas.signedTx": ["hardkas.txReceipt", "hardkas.signedTx", "hardkas.migrationReceipt.v1"],
777
+ "hardkas.txReceipt": ["hardkas.txTrace", "hardkas.migrationReceipt.v1"]
733
778
  };
734
- const allowed = validTransitions[parent.schema] || [];
735
- if (!allowed.includes(artifact.schema)) {
779
+ let isValidTransition = false;
780
+ if (parent.schema === "hardkas.migrationReceipt.v1") {
781
+ isValidTransition = parent.toSchema === artifact.schema;
782
+ } else if (artifact.schema === "hardkas.migrationReceipt.v1") {
783
+ isValidTransition = artifact.fromSchema === parent.schema;
784
+ } else {
785
+ const allowed = validTransitions[parent.schema] || [];
786
+ isValidTransition = allowed.includes(artifact.schema);
787
+ }
788
+ if (!isValidTransition) {
736
789
  addIssue(
737
790
  "INVALID_TRANSITION",
738
791
  `Invalid lineage transition: ${parent.schema} -> ${artifact.schema}`
@@ -764,15 +817,15 @@ function sortUtxosByOutpoint(utxos) {
764
817
  return deterministicCompare2(aId, bId);
765
818
  });
766
819
  }
767
- async function verifyArtifactIntegrity(artifactOrPath) {
820
+ function verifyArtifactIntegritySync(artifactOrPath) {
768
821
  const result = {
769
822
  ok: false,
770
823
  errors: [],
771
824
  issues: []
772
825
  };
773
- const addError = (code, message, path4) => {
826
+ const addError = (code, message, path5) => {
774
827
  result.errors.push(message);
775
- result.issues.push({ code, severity: "error", message, path: path4 });
828
+ result.issues.push({ code, severity: "error", message, path: path5 });
776
829
  };
777
830
  let artifact;
778
831
  try {
@@ -844,6 +897,18 @@ async function verifyArtifactIntegrity(artifactOrPath) {
844
897
  case "hardkas.workflow.v1":
845
898
  schema = WorkflowSchema;
846
899
  break;
900
+ case "hardkas.policy.v1":
901
+ schema = PolicySchema;
902
+ break;
903
+ case "hardkas.networkProfile.v1":
904
+ schema = NetworkProfileSchema;
905
+ break;
906
+ case "hardkas.assumption.v1":
907
+ schema = AssumptionSchema;
908
+ break;
909
+ case "hardkas.migrationReceipt.v1":
910
+ schema = MigrationReceiptSchema;
911
+ break;
847
912
  }
848
913
  if (schema) {
849
914
  const validation = schema.safeParse(v);
@@ -874,6 +939,37 @@ async function verifyArtifactIntegrity(artifactOrPath) {
874
939
  return result;
875
940
  }
876
941
  }
942
+ async function verifyArtifactIntegrity(artifactOrPath) {
943
+ return verifyArtifactIntegritySync(artifactOrPath);
944
+ }
945
+ function findFileByHash(hash, dirs) {
946
+ const shortHash = hash.startsWith("plan-") || hash.startsWith("signed-") ? hash : hash.slice(0, 16);
947
+ for (const dir of dirs) {
948
+ if (!fs.existsSync(dir)) continue;
949
+ try {
950
+ const files = fs.readdirSync(dir);
951
+ if (files.includes(`${hash}.json`)) {
952
+ return path.join(dir, `${hash}.json`);
953
+ }
954
+ for (const file of files) {
955
+ if (!file.endsWith(".json")) continue;
956
+ if (file.includes(hash) || file.includes(shortHash) || file.includes(hash.slice(0, 8))) {
957
+ const filePath = path.join(dir, file);
958
+ try {
959
+ const content = fs.readFileSync(filePath, "utf-8");
960
+ const obj = JSON.parse(content);
961
+ if (obj.contentHash === hash || obj.artifactId === hash || obj.planId === hash || obj.signedId === hash || obj.txId === hash) {
962
+ return filePath;
963
+ }
964
+ } catch {
965
+ }
966
+ }
967
+ }
968
+ } catch {
969
+ }
970
+ }
971
+ return null;
972
+ }
877
973
  function verifyArtifactSemantics(artifact, context = {}) {
878
974
  const result = {
879
975
  ok: true,
@@ -899,6 +995,225 @@ function verifyArtifactSemantics(artifact, context = {}) {
899
995
  });
900
996
  }
901
997
  const v = artifact;
998
+ let parentObj = null;
999
+ if (strict) {
1000
+ const searchDirs = [];
1001
+ if (context.artifactsDir) {
1002
+ searchDirs.push(context.artifactsDir);
1003
+ }
1004
+ searchDirs.push(path.join(process.cwd(), ".hardkas", "artifacts"));
1005
+ searchDirs.push(path.join(process.cwd(), "artifacts"));
1006
+ const policyRefs = [];
1007
+ if (Array.isArray(v.policyRefs)) {
1008
+ policyRefs.push(...v.policyRefs);
1009
+ } else if (typeof v.policyRef === "string") {
1010
+ policyRefs.push(v.policyRef);
1011
+ }
1012
+ for (const ref of policyRefs) {
1013
+ let refObj = context.resolveArtifact ? context.resolveArtifact(ref) : null;
1014
+ if (!refObj) {
1015
+ const refFile = findFileByHash(ref, searchDirs);
1016
+ if (refFile) {
1017
+ try {
1018
+ const content = fs.readFileSync(refFile, "utf-8");
1019
+ refObj = JSON.parse(content);
1020
+ } catch (e) {
1021
+ addIssue({
1022
+ code: "REFERENCE_CORRUPT",
1023
+ severity: "error",
1024
+ message: `Failed to read policy ${ref}: ${e.message}`
1025
+ });
1026
+ }
1027
+ }
1028
+ }
1029
+ if (!refObj) {
1030
+ addIssue({
1031
+ code: "REFERENCE_MISSING",
1032
+ severity: "error",
1033
+ message: `Referenced policy artifact ${ref} not found in workspace`
1034
+ });
1035
+ } else {
1036
+ try {
1037
+ const integrity = verifyArtifactIntegritySync(refObj);
1038
+ if (!integrity.ok) {
1039
+ addIssue({
1040
+ code: "REFERENCE_HASH_MISMATCH",
1041
+ severity: "error",
1042
+ message: `Referenced policy ${ref} integrity check failed: ${integrity.errors.join(", ")}`
1043
+ });
1044
+ } else if (refObj.contentHash !== ref && refObj.artifactId !== ref) {
1045
+ addIssue({
1046
+ code: "REFERENCE_HASH_MISMATCH",
1047
+ severity: "error",
1048
+ message: `Policy reference mismatch: expected ${ref}, got ${refObj.contentHash}`
1049
+ });
1050
+ } else {
1051
+ if (refObj.schema === "hardkas.policy.v1") {
1052
+ if (refObj.decision === "DENY") {
1053
+ addIssue({
1054
+ code: "POLICY_VIOLATION",
1055
+ severity: "error",
1056
+ message: `Policy evaluation rejected: decision is DENY`
1057
+ });
1058
+ } else if (refObj.decision !== "ALLOW") {
1059
+ addIssue({
1060
+ code: "POLICY_VIOLATION",
1061
+ severity: "error",
1062
+ message: `Policy evaluation rejected: decision is invalid (${refObj.decision})`
1063
+ });
1064
+ }
1065
+ const failedRules = refObj.rules?.filter((r) => r.result === "FAIL") || [];
1066
+ if (failedRules.length > 0) {
1067
+ addIssue({
1068
+ code: "POLICY_VIOLATION",
1069
+ severity: "error",
1070
+ message: `Policy rules failed: ${failedRules.map((r) => r.id).join(", ")}`
1071
+ });
1072
+ }
1073
+ }
1074
+ }
1075
+ } catch (e) {
1076
+ addIssue({
1077
+ code: "REFERENCE_CORRUPT",
1078
+ severity: "error",
1079
+ message: `Failed to read/verify policy ${ref}: ${e.message}`
1080
+ });
1081
+ }
1082
+ }
1083
+ }
1084
+ if (typeof v.networkProfileRef === "string") {
1085
+ const ref = v.networkProfileRef;
1086
+ let refObj = context.resolveArtifact ? context.resolveArtifact(ref) : null;
1087
+ if (!refObj) {
1088
+ const refFile = findFileByHash(ref, searchDirs);
1089
+ if (refFile) {
1090
+ try {
1091
+ refObj = JSON.parse(fs.readFileSync(refFile, "utf-8"));
1092
+ } catch {
1093
+ }
1094
+ }
1095
+ }
1096
+ if (!refObj) {
1097
+ addIssue({
1098
+ code: "REFERENCE_MISSING",
1099
+ severity: "error",
1100
+ message: `Referenced network profile ${ref} not found in workspace`
1101
+ });
1102
+ } else {
1103
+ try {
1104
+ const integrity = verifyArtifactIntegritySync(refObj);
1105
+ if (!integrity.ok) {
1106
+ addIssue({
1107
+ code: "REFERENCE_HASH_MISMATCH",
1108
+ severity: "error",
1109
+ message: `Referenced profile ${ref} integrity check failed`
1110
+ });
1111
+ } else if (refObj.contentHash !== ref && refObj.artifactId !== ref) {
1112
+ addIssue({
1113
+ code: "REFERENCE_HASH_MISMATCH",
1114
+ severity: "error",
1115
+ message: `Profile reference mismatch: expected ${ref}, got ${refObj.contentHash}`
1116
+ });
1117
+ }
1118
+ } catch (e) {
1119
+ addIssue({
1120
+ code: "REFERENCE_CORRUPT",
1121
+ severity: "error",
1122
+ message: `Failed to verify profile ${ref}`
1123
+ });
1124
+ }
1125
+ }
1126
+ }
1127
+ if (typeof v.assumptionRef === "string") {
1128
+ const ref = v.assumptionRef;
1129
+ let refObj = context.resolveArtifact ? context.resolveArtifact(ref) : null;
1130
+ if (!refObj) {
1131
+ const refFile = findFileByHash(ref, searchDirs);
1132
+ if (refFile) {
1133
+ try {
1134
+ refObj = JSON.parse(fs.readFileSync(refFile, "utf-8"));
1135
+ } catch {
1136
+ }
1137
+ }
1138
+ }
1139
+ if (!refObj) {
1140
+ addIssue({
1141
+ code: "REFERENCE_MISSING",
1142
+ severity: "error",
1143
+ message: `Referenced assumption ${ref} not found in workspace`
1144
+ });
1145
+ } else {
1146
+ try {
1147
+ const integrity = verifyArtifactIntegritySync(refObj);
1148
+ if (!integrity.ok) {
1149
+ addIssue({
1150
+ code: "REFERENCE_HASH_MISMATCH",
1151
+ severity: "error",
1152
+ message: `Referenced assumption ${ref} integrity check failed`
1153
+ });
1154
+ } else if (refObj.contentHash !== ref && refObj.artifactId !== ref) {
1155
+ addIssue({
1156
+ code: "REFERENCE_HASH_MISMATCH",
1157
+ severity: "error",
1158
+ message: `Assumption reference mismatch: expected ${ref}, got ${refObj.contentHash}`
1159
+ });
1160
+ }
1161
+ } catch (e) {
1162
+ addIssue({
1163
+ code: "REFERENCE_CORRUPT",
1164
+ severity: "error",
1165
+ message: `Failed to verify assumption ${ref}`
1166
+ });
1167
+ }
1168
+ }
1169
+ }
1170
+ let parentId;
1171
+ const lineage = v.lineage;
1172
+ if (lineage?.parentArtifactId && lineage?.parentArtifactId !== lineage?.artifactId) {
1173
+ parentId = lineage?.parentArtifactId;
1174
+ } else if (v.schema === "hardkas.signedTx") {
1175
+ parentId = v.sourcePlanId;
1176
+ } else if (v.schema === "hardkas.txReceipt") {
1177
+ parentId = v.sourceSignedId || lineage?.parentArtifactId;
1178
+ }
1179
+ if (parentId) {
1180
+ parentObj = context.resolveArtifact ? context.resolveArtifact(parentId) : null;
1181
+ if (!parentObj) {
1182
+ const parentFile = findFileByHash(parentId, searchDirs);
1183
+ if (parentFile) {
1184
+ try {
1185
+ parentObj = JSON.parse(fs.readFileSync(parentFile, "utf-8"));
1186
+ } catch (e) {
1187
+ addIssue({
1188
+ code: "PARENT_CORRUPT",
1189
+ severity: "error",
1190
+ message: `Failed to read parent ${parentId}: ${e.message}`
1191
+ });
1192
+ }
1193
+ }
1194
+ }
1195
+ if (!parentObj) {
1196
+ addIssue({
1197
+ code: "PARENT_MISSING",
1198
+ severity: "warning",
1199
+ message: `Parent artifact ${parentId} not found in workspace`
1200
+ });
1201
+ } else {
1202
+ try {
1203
+ const parentSem = verifyArtifactSemantics(parentObj, { ...context, strict: true });
1204
+ if (!parentSem.ok) {
1205
+ parentSem.issues.forEach((issue) => addIssue(issue));
1206
+ }
1207
+ } catch (e) {
1208
+ addIssue({
1209
+ code: "PARENT_CORRUPT",
1210
+ severity: "error",
1211
+ message: `Failed to verify parent ${parentId}: ${e.message}`
1212
+ });
1213
+ }
1214
+ }
1215
+ }
1216
+ }
902
1217
  if (v.createdAt && typeof v.createdAt === "string") {
903
1218
  const created = new Date(v.createdAt).getTime();
904
1219
  const now = clock.now();
@@ -917,31 +1232,47 @@ function verifyArtifactSemantics(artifact, context = {}) {
917
1232
  });
918
1233
  }
919
1234
  }
920
- const lineageAudit = verifyLineage(v, context.parent, { strict });
1235
+ const lineageAudit = verifyLineage(v, parentObj || context.parent, { strict });
921
1236
  if (!lineageAudit.ok || strict && !v.lineage && v.schema !== "hardkas.workflow.v1") {
922
1237
  lineageAudit.issues.forEach((issue) => {
923
1238
  addIssue(issue);
924
1239
  });
925
1240
  }
926
1241
  if (strict) {
927
- if (!v.workflowId)
928
- addIssue({
929
- code: "MISSING_WORKFLOW_ID",
930
- severity: "error",
931
- message: "Strict mode requires workflowId"
932
- });
933
- if (!v.assumptionLevel && v.schema !== "hardkas.workflow.v1")
934
- addIssue({
935
- code: "MISSING_ASSUMPTION_LEVEL",
936
- severity: "error",
937
- message: "Strict mode requires assumptionLevel"
938
- });
939
- if (!v.executionMode && !v.mode)
940
- addIssue({
941
- code: "MISSING_EXECUTION_MODE",
942
- severity: "error",
943
- message: "Strict mode requires executionMode"
944
- });
1242
+ const enforceMetadata = context.enforceMetadata ?? true;
1243
+ if (enforceMetadata) {
1244
+ if (!v.workflowId)
1245
+ addIssue({
1246
+ code: "MISSING_WORKFLOW_ID",
1247
+ severity: "error",
1248
+ message: "Strict mode requires workflowId"
1249
+ });
1250
+ if (!v.assumptionLevel && v.schema !== "hardkas.workflow.v1")
1251
+ addIssue({
1252
+ code: "MISSING_ASSUMPTION_LEVEL",
1253
+ severity: "error",
1254
+ message: "Strict mode requires assumptionLevel"
1255
+ });
1256
+ if (!v.executionMode && !v.mode)
1257
+ addIssue({
1258
+ code: "MISSING_EXECUTION_MODE",
1259
+ severity: "error",
1260
+ message: "Strict mode requires executionMode"
1261
+ });
1262
+ } else {
1263
+ if (!v.workflowId)
1264
+ addIssue({
1265
+ code: "MISSING_WORKFLOW_ID",
1266
+ severity: "warning",
1267
+ message: "Missing workflowId"
1268
+ });
1269
+ if (!v.assumptionLevel && v.schema !== "hardkas.workflow.v1")
1270
+ addIssue({
1271
+ code: "MISSING_ASSUMPTION_LEVEL",
1272
+ severity: "warning",
1273
+ message: "Missing assumptionLevel"
1274
+ });
1275
+ }
945
1276
  } else {
946
1277
  if (!v.workflowId)
947
1278
  addIssue({
@@ -1214,6 +1545,16 @@ var InvariantWatcher = class {
1214
1545
  };
1215
1546
 
1216
1547
  // src/migration.ts
1548
+ var MigrationRequiredError = class extends Error {
1549
+ constructor(oldVersion, targetVersion) {
1550
+ super(`MIGRATION_REQUIRED: Artifact requires explicit migration from ${oldVersion} to ${targetVersion}`);
1551
+ this.oldVersion = oldVersion;
1552
+ this.targetVersion = targetVersion;
1553
+ this.name = "MigrationRequiredError";
1554
+ }
1555
+ oldVersion;
1556
+ targetVersion;
1557
+ };
1217
1558
  var migrationRegistry = [];
1218
1559
  function registerMigrationStep(step) {
1219
1560
  const existing = migrationRegistry.find(
@@ -1235,6 +1576,9 @@ registerMigrationStep({
1235
1576
  description: "Legacy v1 schemas to canonical 1.0.0-alpha format",
1236
1577
  transform(artifact) {
1237
1578
  const migrated = { ...artifact };
1579
+ if (migrated.lineage) {
1580
+ migrated.lineage = { ...migrated.lineage };
1581
+ }
1238
1582
  if (typeof migrated.schema === "string" && migrated.schema.endsWith(".v1")) {
1239
1583
  if (migrated.schema !== "hardkas.workflow.v1") {
1240
1584
  migrated.schema = migrated.schema.replace(/\.v1$/, "");
@@ -1299,10 +1643,10 @@ function canMigrate(artifact, targetVersion = ARTIFACT_VERSION) {
1299
1643
  if (currentVersion === targetVersion) {
1300
1644
  return true;
1301
1645
  }
1302
- const path4 = getMigrationPath(currentVersion, targetVersion);
1303
- return path4.length > 0;
1646
+ const path5 = getMigrationPath(currentVersion, targetVersion);
1647
+ return path5.length > 0;
1304
1648
  }
1305
- function migrateArtifactPayload(artifact, targetVersion = ARTIFACT_VERSION) {
1649
+ function migrateArtifactPayload(artifact, targetVersion = ARTIFACT_VERSION, options) {
1306
1650
  const currentVersion = detectArtifactVersion(artifact);
1307
1651
  if (currentVersion === targetVersion) {
1308
1652
  return {
@@ -1312,8 +1656,11 @@ function migrateArtifactPayload(artifact, targetVersion = ARTIFACT_VERSION) {
1312
1656
  appliedSteps: []
1313
1657
  };
1314
1658
  }
1315
- const path4 = getMigrationPath(currentVersion, targetVersion);
1316
- if (path4.length === 0) {
1659
+ if (options?.strictPolicy) {
1660
+ throw new MigrationRequiredError(currentVersion, targetVersion);
1661
+ }
1662
+ const path5 = getMigrationPath(currentVersion, targetVersion);
1663
+ if (path5.length === 0) {
1317
1664
  throw new Error(
1318
1665
  `No migration path from version "${currentVersion}" to "${targetVersion}". Registered steps: [${migrationRegistry.map((s) => `${s.fromVersion}\u2192${s.toVersion}`).join(", ")}]`
1319
1666
  );
@@ -1322,7 +1669,7 @@ function migrateArtifactPayload(artifact, targetVersion = ARTIFACT_VERSION) {
1322
1669
  const originalLineage = artifact.lineage;
1323
1670
  let current = { ...artifact };
1324
1671
  const appliedSteps = [];
1325
- for (const step of path4) {
1672
+ for (const step of path5) {
1326
1673
  current = step.transform(current);
1327
1674
  appliedSteps.push({
1328
1675
  fromVersion: step.fromVersion,
@@ -1355,10 +1702,51 @@ function migrateToCanonical(v1Artifact) {
1355
1702
  const result = migrateArtifactPayload(v1Artifact, ARTIFACT_VERSION);
1356
1703
  return result.artifact;
1357
1704
  }
1705
+ function generateMigrationReceipt(oldArtifact, newArtifact, migrationId) {
1706
+ const oldHash = oldArtifact.contentHash || calculateContentHash(oldArtifact, CURRENT_HASH_VERSION);
1707
+ const newHash = newArtifact.contentHash || calculateContentHash(newArtifact, CURRENT_HASH_VERSION);
1708
+ const receipt = {
1709
+ schema: "hardkas.migrationReceipt.v1",
1710
+ hardkasVersion: HARDKAS_VERSION,
1711
+ version: ARTIFACT_VERSION,
1712
+ hashVersion: CURRENT_HASH_VERSION,
1713
+ networkId: oldArtifact.networkId || "simnet",
1714
+ mode: oldArtifact.mode || "simulated",
1715
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1716
+ oldHash,
1717
+ newHash,
1718
+ fromSchema: oldArtifact.schema || "unknown",
1719
+ toSchema: newArtifact.schema || "unknown",
1720
+ migrationId,
1721
+ decision: "MIGRATED_WITH_PROOF",
1722
+ lineage: {
1723
+ artifactId: "",
1724
+ // Filled after hash
1725
+ lineageId: oldArtifact.lineage?.lineageId || ("migration" + oldHash).padEnd(64, "0").slice(0, 64),
1726
+ parentArtifactId: oldHash,
1727
+ rootArtifactId: oldArtifact.lineage?.rootArtifactId || oldHash
1728
+ }
1729
+ };
1730
+ receipt.contentHash = calculateContentHash(receipt, CURRENT_HASH_VERSION);
1731
+ receipt.lineage.artifactId = receipt.contentHash;
1732
+ if (!newArtifact.lineage) {
1733
+ newArtifact.lineage = {
1734
+ artifactId: newHash,
1735
+ lineageId: receipt.lineage.lineageId,
1736
+ parentArtifactId: receipt.contentHash,
1737
+ rootArtifactId: receipt.lineage.rootArtifactId
1738
+ };
1739
+ } else {
1740
+ newArtifact.lineage.parentArtifactId = receipt.contentHash;
1741
+ }
1742
+ newArtifact.contentHash = calculateContentHash(newArtifact, CURRENT_HASH_VERSION);
1743
+ newArtifact.lineage.artifactId = newArtifact.contentHash;
1744
+ return receipt;
1745
+ }
1358
1746
 
1359
1747
  // src/io.ts
1360
1748
  import fs2 from "fs/promises";
1361
- import path from "path";
1749
+ import path2 from "path";
1362
1750
  import { writeFileAtomic } from "@hardkas/core";
1363
1751
  var bigIntReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
1364
1752
  async function writeArtifact(filePath, artifact) {
@@ -1369,10 +1757,10 @@ async function writeArtifact(filePath, artifact) {
1369
1757
  const artifactObj = typeof artifact === "string" ? JSON.parse(artifact) : artifact;
1370
1758
  const id = artifactObj.planId || artifactObj.signedId || artifactObj.txId || Date.now().toString(36);
1371
1759
  const prefix = artifactObj.schema ? artifactObj.schema.split(".")[1] || "artifact" : "artifact";
1372
- targetPath = path.join(filePath, `${prefix}-${id}.json`);
1760
+ targetPath = path2.join(filePath, `${prefix}-${id}.json`);
1373
1761
  console.log(
1374
1762
  `
1375
- Note: Provided path is a directory. Auto-generating artifact filename: ${path.basename(targetPath)}`
1763
+ Note: Provided path is a directory. Auto-generating artifact filename: ${path2.basename(targetPath)}`
1376
1764
  );
1377
1765
  }
1378
1766
  } catch (e) {
@@ -1380,14 +1768,14 @@ Note: Provided path is a directory. Auto-generating artifact filename: ${path.ba
1380
1768
  const artifactObj = typeof artifact === "string" ? JSON.parse(artifact) : artifact;
1381
1769
  const id = artifactObj.planId || artifactObj.signedId || artifactObj.txId || Date.now().toString(36);
1382
1770
  const prefix = artifactObj.schema ? artifactObj.schema.split(".")[1] || "artifact" : "artifact";
1383
- targetPath = path.join(filePath, `${prefix}-${id}.json`);
1771
+ targetPath = path2.join(filePath, `${prefix}-${id}.json`);
1384
1772
  }
1385
1773
  }
1386
1774
  const content = typeof artifact === "string" ? artifact : JSON.stringify(artifact, bigIntReplacer, 2) + "\n";
1387
1775
  await writeFileAtomic(targetPath, content);
1388
1776
  }
1389
1777
  function getDefaultReceiptPath(txId, cwd = process.cwd()) {
1390
- return path.join(cwd, "artifacts", "receipts", `${txId}.json`);
1778
+ return path2.join(cwd, "artifacts", "receipts", `${txId}.json`);
1391
1779
  }
1392
1780
  async function readArtifact(filePath) {
1393
1781
  try {
@@ -1593,6 +1981,14 @@ function createTxPlanArtifact(options) {
1593
1981
  amountSompi: o.amountSompi.toString()
1594
1982
  })),
1595
1983
  rpcUrl: options.rpcUrl,
1984
+ lineage: {
1985
+ artifactId: "",
1986
+ lineageId: "0".repeat(64),
1987
+ // placeholder, will be replaced if empty
1988
+ parentArtifactId: "",
1989
+ rootArtifactId: "",
1990
+ sequence: 1
1991
+ },
1596
1992
  ...options.ctx.workflowId ? { workflowId: options.ctx.workflowId } : {}
1597
1993
  };
1598
1994
  if (options.plan.change) {
@@ -1604,6 +2000,18 @@ function createTxPlanArtifact(options) {
1604
2000
  const hash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1605
2001
  artifact.planId = `plan-${hash.slice(0, 16)}`;
1606
2002
  artifact.contentHash = hash;
2003
+ if (artifact.lineage) {
2004
+ artifact.lineage.lineageId = artifact.contentHash;
2005
+ artifact.lineage.artifactId = artifact.contentHash;
2006
+ artifact.lineage.parentArtifactId = artifact.contentHash;
2007
+ artifact.lineage.rootArtifactId = artifact.contentHash;
2008
+ const finalHash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
2009
+ artifact.planId = `plan-${finalHash.slice(0, 16)}`;
2010
+ artifact.contentHash = finalHash;
2011
+ artifact.lineage.artifactId = finalHash;
2012
+ artifact.lineage.parentArtifactId = finalHash;
2013
+ artifact.lineage.rootArtifactId = finalHash;
2014
+ }
1607
2015
  return artifact;
1608
2016
  }
1609
2017
 
@@ -1628,11 +2036,21 @@ function createSimulatedSignedTxArtifact(plan, payload, ctx) {
1628
2036
  format: "simulated",
1629
2037
  payload
1630
2038
  },
2039
+ lineage: {
2040
+ artifactId: "",
2041
+ lineageId: plan.lineage?.lineageId || plan.contentHash || "0".repeat(64),
2042
+ parentArtifactId: plan.contentHash || "0".repeat(64),
2043
+ rootArtifactId: plan.lineage?.rootArtifactId || plan.contentHash || "0".repeat(64),
2044
+ sequence: (plan.lineage?.sequence || 0) + 1
2045
+ },
1631
2046
  ...plan.workflowId ? { workflowId: plan.workflowId } : {}
1632
2047
  };
1633
2048
  const hash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1634
2049
  artifact.signedId = `signed-${hash.slice(0, 16)}`;
1635
2050
  artifact.contentHash = hash;
2051
+ if (artifact.lineage) {
2052
+ artifact.lineage.artifactId = hash;
2053
+ }
1636
2054
  return artifact;
1637
2055
  }
1638
2056
  function createSimulatedTxReceipt(plan, txId, ctx, extra) {
@@ -1658,10 +2076,22 @@ function createSimulatedTxReceipt(plan, txId, ctx, extra) {
1658
2076
  preStateHash: extra?.preStateHash,
1659
2077
  postStateHash: extra?.postStateHash,
1660
2078
  dagContext: extra?.dagContext,
2079
+ lineage: {
2080
+ artifactId: "",
2081
+ lineageId: plan.lineage?.lineageId || plan.contentHash || "0".repeat(64),
2082
+ parentArtifactId: "0".repeat(64),
2083
+ // Will set to contentHash of signedTx later
2084
+ rootArtifactId: plan.lineage?.rootArtifactId || plan.contentHash || "0".repeat(64),
2085
+ sequence: (plan.lineage?.sequence || 1) + 1
2086
+ },
1661
2087
  ...plan.workflowId ? { workflowId: plan.workflowId } : {}
1662
2088
  };
1663
2089
  const hash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1664
2090
  artifact.contentHash = hash;
2091
+ if (artifact.lineage) {
2092
+ artifact.lineage.artifactId = hash;
2093
+ artifact.lineage.parentArtifactId = extra?.preStateHash || txId.replace("simulated-", "").replace("-tx", "").padEnd(64, "0").slice(0, 64);
2094
+ }
1665
2095
  return artifact;
1666
2096
  }
1667
2097
  function getBroadcastableSignedTransaction(artifact) {
@@ -1878,15 +2308,15 @@ async function explainArtifact(artifactUnknown) {
1878
2308
 
1879
2309
  // src/igra-io.ts
1880
2310
  import fs3 from "fs/promises";
1881
- import path2 from "path";
2311
+ import path3 from "path";
1882
2312
  import { deterministicCompare as deterministicCompare3 } from "@hardkas/core";
1883
2313
  function getDefaultL2ReceiptsDir(cwd = process.cwd()) {
1884
- return path2.join(cwd, ".hardkas", "l2-receipts");
2314
+ return path3.join(cwd, ".hardkas", "l2-receipts");
1885
2315
  }
1886
2316
  function getL2ReceiptPath(txHash, options) {
1887
2317
  validateTxHash(txHash);
1888
2318
  const dir = getDefaultL2ReceiptsDir(options?.cwd);
1889
- return path2.join(dir, `${txHash}.igra.receipt.json`);
2319
+ return path3.join(dir, `${txHash}.igra.receipt.json`);
1890
2320
  }
1891
2321
  async function saveIgraTxReceiptArtifact(receipt, options) {
1892
2322
  assertValidIgraTxReceiptArtifact(receipt);
@@ -1908,7 +2338,7 @@ async function listIgraTxReceiptArtifacts(options) {
1908
2338
  const receipts = [];
1909
2339
  for (const file of receiptFiles) {
1910
2340
  try {
1911
- const data = await readArtifact(path2.join(dir, file));
2341
+ const data = await readArtifact(path3.join(dir, file));
1912
2342
  if (data && typeof data === "object" && data.schema === "hardkas.igraTxReceipt.v1") {
1913
2343
  receipts.push(data);
1914
2344
  }
@@ -1941,26 +2371,26 @@ function diffArtifacts(left, right) {
1941
2371
  entries
1942
2372
  };
1943
2373
  }
1944
- function compareRecursive(left, right, path4, entries) {
2374
+ function compareRecursive(left, right, path5, entries) {
1945
2375
  if (isPrimitive(left) || isPrimitive(right)) {
1946
2376
  if (left !== right) {
1947
- entries.push({ path: path4, kind: "changed", left, right });
2377
+ entries.push({ path: path5, kind: "changed", left, right });
1948
2378
  }
1949
2379
  return;
1950
2380
  }
1951
2381
  if (Array.isArray(left) || Array.isArray(right)) {
1952
2382
  if (!Array.isArray(left) || !Array.isArray(right)) {
1953
- entries.push({ path: path4, kind: "changed", left, right });
2383
+ entries.push({ path: path5, kind: "changed", left, right });
1954
2384
  return;
1955
2385
  }
1956
2386
  const maxLength = Math.max(left.length, right.length);
1957
2387
  for (let i = 0; i < maxLength; i++) {
1958
2388
  if (i >= left.length) {
1959
- entries.push({ path: `${path4}[${i}]`, kind: "added", right: right[i] });
2389
+ entries.push({ path: `${path5}[${i}]`, kind: "added", right: right[i] });
1960
2390
  } else if (i >= right.length) {
1961
- entries.push({ path: `${path4}[${i}]`, kind: "removed", left: left[i] });
2391
+ entries.push({ path: `${path5}[${i}]`, kind: "removed", left: left[i] });
1962
2392
  } else {
1963
- compareRecursive(left[i], right[i], `${path4}[${i}]`, entries);
2393
+ compareRecursive(left[i], right[i], `${path5}[${i}]`, entries);
1964
2394
  }
1965
2395
  }
1966
2396
  return;
@@ -1969,7 +2399,7 @@ function compareRecursive(left, right, path4, entries) {
1969
2399
  const rightKeys = Object.keys(right).filter((k) => !SEMANTIC_EXCLUSIONS.has(k));
1970
2400
  const allKeys = /* @__PURE__ */ new Set([...leftKeys, ...rightKeys]);
1971
2401
  for (const key of allKeys) {
1972
- const nextPath = path4 === "$" ? key : `${path4}.${key}`;
2402
+ const nextPath = path5 === "$" ? key : `${path5}.${key}`;
1973
2403
  if (!leftKeys.includes(key)) {
1974
2404
  entries.push({ path: nextPath, kind: "added", right: right[key] });
1975
2405
  } else if (!rightKeys.includes(key)) {
@@ -2033,16 +2463,16 @@ function updateDeploymentStatus(record, newStatus, txId) {
2033
2463
  // src/deployment-store.ts
2034
2464
  import fs4 from "fs/promises";
2035
2465
  import { existsSync } from "fs";
2036
- import path3 from "path";
2466
+ import path4 from "path";
2037
2467
  import { writeFileAtomic as writeFileAtomic2 } from "@hardkas/core";
2038
2468
  async function saveDeployment(rootDir, record) {
2039
- const deploymentsDir = path3.join(rootDir, ".hardkas", "deployments", record.networkId);
2040
- const targetPath = path3.join(deploymentsDir, `${record.label}.json`);
2469
+ const deploymentsDir = path4.join(rootDir, ".hardkas", "deployments", record.networkId);
2470
+ const targetPath = path4.join(deploymentsDir, `${record.label}.json`);
2041
2471
  await writeFileAtomic2(targetPath, JSON.stringify(record, null, 2));
2042
2472
  return targetPath;
2043
2473
  }
2044
2474
  async function loadDeployment(rootDir, networkId, label) {
2045
- const targetPath = path3.join(
2475
+ const targetPath = path4.join(
2046
2476
  rootDir,
2047
2477
  ".hardkas",
2048
2478
  "deployments",
@@ -2054,18 +2484,18 @@ async function loadDeployment(rootDir, networkId, label) {
2054
2484
  return JSON.parse(content);
2055
2485
  }
2056
2486
  async function listDeployments(rootDir, networkId) {
2057
- const baseDir = path3.join(rootDir, ".hardkas", "deployments");
2487
+ const baseDir = path4.join(rootDir, ".hardkas", "deployments");
2058
2488
  if (!existsSync(baseDir)) return [];
2059
2489
  const summaries = [];
2060
2490
  const networks = networkId ? [networkId] : await fs4.readdir(baseDir);
2061
2491
  for (const net of networks) {
2062
- const netDir = path3.join(baseDir, net);
2492
+ const netDir = path4.join(baseDir, net);
2063
2493
  if (!existsSync(netDir)) continue;
2064
2494
  const files = await fs4.readdir(netDir);
2065
2495
  for (const file of files) {
2066
2496
  if (!file.endsWith(".json")) continue;
2067
2497
  try {
2068
- const content = await fs4.readFile(path3.join(netDir, file), "utf-8");
2498
+ const content = await fs4.readFile(path4.join(netDir, file), "utf-8");
2069
2499
  const record = JSON.parse(content);
2070
2500
  const summary = {
2071
2501
  label: record.label,
@@ -2098,7 +2528,7 @@ async function updateDeployment(rootDir, networkId, label, update) {
2098
2528
  return updated;
2099
2529
  }
2100
2530
  async function deleteDeployment(rootDir, networkId, label) {
2101
- const targetPath = path3.join(
2531
+ const targetPath = path4.join(
2102
2532
  rootDir,
2103
2533
  ".hardkas",
2104
2534
  "deployments",
@@ -2114,6 +2544,7 @@ export {
2114
2544
  ARTIFACT_VERSION,
2115
2545
  AccountRefSchema,
2116
2546
  ArtifactLineageSchema,
2547
+ AssumptionSchema,
2117
2548
  BaseArtifactSchema,
2118
2549
  BasicCorrelationInvariant,
2119
2550
  BasicLineageInvariant,
@@ -2124,7 +2555,11 @@ export {
2124
2555
  InvariantWatcher,
2125
2556
  LifecycleInvariant,
2126
2557
  LocalnetUtxoSchemaV2,
2558
+ MigrationReceiptSchema,
2559
+ MigrationRequiredError,
2127
2560
  NetworkInvariant,
2561
+ NetworkProfileSchema,
2562
+ PolicySchema,
2128
2563
  ReplayInvariant,
2129
2564
  RuntimeSessionSchema,
2130
2565
  SEMANTIC_EXCLUSIONS,
@@ -2169,6 +2604,7 @@ export {
2169
2604
  formatSignedTxArtifact,
2170
2605
  formatTxPlanArtifact,
2171
2606
  formatTxReceiptArtifact,
2607
+ generateMigrationReceipt,
2172
2608
  getBroadcastableSignedTransaction,
2173
2609
  getDefaultL2ReceiptsDir,
2174
2610
  getDefaultReceiptPath,
@@ -2207,6 +2643,7 @@ export {
2207
2643
  verifyArtifact,
2208
2644
  verifyArtifactFile,
2209
2645
  verifyArtifactIntegrity,
2646
+ verifyArtifactIntegritySync,
2210
2647
  verifyArtifactReplay,
2211
2648
  verifyArtifactSemantics,
2212
2649
  verifyFeeSemantics,