@tradejs/cli 3.1.16-beta.230 → 3.1.17-beta.231

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.
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/lib/runtimeScorecard.ts
@@ -36,21 +26,7 @@ __export(runtimeScorecard_exports, {
36
26
  module.exports = __toCommonJS(runtimeScorecard_exports);
37
27
 
38
28
  // src/lib/runtimeLineage.ts
39
- var import_node_child_process = require("child_process");
40
- var import_node_crypto2 = require("crypto");
41
- var import_promises2 = __toESM(require("fs/promises"));
42
- var import_node_path2 = __toESM(require("path"));
43
- var import_types = require("@tradejs/types");
44
- var import_strategies = require("@tradejs/node/strategies");
45
- var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
46
-
47
- // src/lib/strategyGateFingerprint.ts
48
29
  var import_node_crypto = require("crypto");
49
- var import_node_module = require("module");
50
- var import_promises = __toESM(require("fs/promises"));
51
- var import_node_path = __toESM(require("path"));
52
-
53
- // src/lib/runtimeLineage.ts
54
30
  var normalizeForStableJson = (value) => {
55
31
  if (Array.isArray(value)) {
56
32
  return value.map(normalizeForStableJson);
@@ -62,26 +38,174 @@ var normalizeForStableJson = (value) => {
62
38
  }
63
39
  return value;
64
40
  };
65
- var fingerprintRuntimeValue = (value) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
66
- var runtimeLineageKey = (lineage) => lineage.schemaVersion === 3 ? [
41
+ var fingerprintRuntimeValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
42
+ var runtimeLineageKey = (lineage) => [
67
43
  "v3",
68
44
  lineage.deploymentCompositionId,
69
45
  lineage.strategyRevision,
70
46
  lineage.strategyPackageVersion,
71
- `deps:${fingerprintRuntimeValue(
72
- lineage.strategyDependencyVersions
73
- ).slice(0, 16)}`,
47
+ `deps:${fingerprintRuntimeValue(lineage.strategyDependencyVersions).slice(0, 16)}`,
74
48
  lineage.runtimePackageVersion
75
- ].join(":") : [
76
- lineage.schemaVersion,
77
- lineage.compositionId ?? "unbound-composition",
78
- lineage.gitSha ?? "unknown",
79
- lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
80
- lineage.gateFingerprint,
81
- lineage.configFingerprint,
82
- lineage.contextFingerprint
83
49
  ].join(":");
84
50
 
51
+ // src/lib/runtimeEvidenceDeployment.ts
52
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
53
+ var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
54
+ var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
55
+ var isPackageVersionMap = (value) => isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(
56
+ ([name, version]) => name.startsWith("@tradejs/") && isNonEmptyString(version)
57
+ );
58
+ var INTERVALS = /* @__PURE__ */ new Set([
59
+ "1",
60
+ "3",
61
+ "5",
62
+ "15",
63
+ "30",
64
+ "60",
65
+ "120",
66
+ "240",
67
+ "360",
68
+ "720",
69
+ "D",
70
+ "W",
71
+ "M"
72
+ ]);
73
+ var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
74
+ var isRuntimeStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => key === "tickers") && Array.isArray(value.tickers) && value.tickers.length > 0 && value.tickers.every((ticker) => isNonEmptyString(ticker));
75
+ var samePackageVersions = (left, right) => {
76
+ if (!left || !right) return false;
77
+ const leftEntries = Object.entries(left).sort(
78
+ ([a], [b]) => a.localeCompare(b)
79
+ );
80
+ const rightEntries = Object.entries(right).sort(
81
+ ([a], [b]) => a.localeCompare(b)
82
+ );
83
+ return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
84
+ };
85
+ var runtimeLineageMatchesStrategySnapshot = ({
86
+ lineage,
87
+ deployment,
88
+ strategy
89
+ }) => lineage?.schemaVersion === 3 && lineage.strategyRevision === strategy.strategyRevision && lineage.deploymentCompositionId === deployment.deploymentCompositionId && lineage.strategyPackageVersion === strategy.strategyPackageVersion && lineage.runtimePackageVersion === strategy.runtimePackageVersion && samePackageVersions(
90
+ lineage.strategyDependencyVersions,
91
+ strategy.strategyDependencyVersions
92
+ );
93
+ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
94
+ if (!isRecord(value) || value.schemaVersion !== 2 || !isNonEmptyString(value.id) || !isNonEmptyString(value.deploymentCompositionId) || !/^dc1:[a-f0-9]{16}$/.test(value.deploymentCompositionId) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
95
+ throw new Error(
96
+ "Runtime evidence deployment snapshot is missing or invalid"
97
+ );
98
+ }
99
+ const strategies = value.strategies.map((candidate) => {
100
+ if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !isNonEmptyString(candidate.strategyRevision) || !/^sr1:[a-f0-9]{16}$/.test(candidate.strategyRevision) || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isPackageVersionMap(candidate.strategyDependencyVersions) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig) || !isRuntimeStrategySelection(candidate.selection)) {
101
+ throw new Error(
102
+ "Runtime evidence deployment strategy snapshot is invalid"
103
+ );
104
+ }
105
+ if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
106
+ throw new Error(
107
+ `Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
108
+ );
109
+ }
110
+ if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
111
+ throw new Error(
112
+ `Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
113
+ );
114
+ }
115
+ return candidate;
116
+ });
117
+ const identities = /* @__PURE__ */ new Set();
118
+ for (const strategy of strategies) {
119
+ if (identities.has(strategy.strategyName)) {
120
+ throw new Error(
121
+ `Duplicate runtime evidence strategy: ${strategy.strategyName}`
122
+ );
123
+ }
124
+ identities.add(strategy.strategyName);
125
+ }
126
+ return {
127
+ ...value,
128
+ strategies
129
+ };
130
+ };
131
+ var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
132
+ var unwrapRuntimeEvidenceRows = (value, field) => (Array.isArray(value) ? value : []).map((candidate) => {
133
+ const record = isRecord(candidate) ? candidate : null;
134
+ const nested = record && isRecord(record[field]) ? record[field] : null;
135
+ return nested ?? record;
136
+ }).filter((record) => record != null);
137
+ var assertCurrentRuntimeEvidenceArtifact = (artifact) => {
138
+ if (!isRecord(artifact) || artifact.reportType !== "runtime-evidence") {
139
+ throw new Error("Invalid runtime evidence artifact");
140
+ }
141
+ const deployment = parseRuntimeEvidenceDeploymentSnapshot(
142
+ artifact.deployment
143
+ );
144
+ const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
145
+ if (activeStrategies.length === 0) {
146
+ throw new Error("Runtime evidence deployment has no enabled strategies");
147
+ }
148
+ const runtime = isRecord(artifact.runtime) ? artifact.runtime : null;
149
+ if (!runtime) {
150
+ throw new Error("Runtime evidence payload is missing");
151
+ }
152
+ if ("strategyConfigs" in runtime) {
153
+ throw new Error(
154
+ "Runtime evidence payload contains removed strategyConfigs data"
155
+ );
156
+ }
157
+ const rowFields = [
158
+ "trades",
159
+ "signals",
160
+ "evaluations",
161
+ "lineageScopes"
162
+ ];
163
+ for (const field of rowFields) {
164
+ if (!Array.isArray(runtime[field])) {
165
+ throw new Error(`Runtime evidence payload ${field} must be an array`);
166
+ }
167
+ }
168
+ const snapshots = new Map(
169
+ activeStrategies.map((strategy) => [strategy.strategyName, strategy])
170
+ );
171
+ const assertRows = (label, rows, lineageField) => {
172
+ for (const row of rows) {
173
+ const strategyName = String(row.strategy ?? "").trim();
174
+ const strategy = snapshots.get(strategyName);
175
+ if (!strategy || row.deploymentId !== deployment.id || row.accountId !== deployment.accountId || !runtimeLineageMatchesStrategySnapshot({
176
+ lineage: isRecord(row[lineageField]) ? row[lineageField] : void 0,
177
+ deployment,
178
+ strategy
179
+ })) {
180
+ throw new Error(
181
+ `Runtime evidence ${label} row is outside the current embedded deployment lineage`
182
+ );
183
+ }
184
+ }
185
+ };
186
+ assertRows(
187
+ "trade",
188
+ unwrapRuntimeEvidenceRows(runtime.trades, "trade"),
189
+ "runtimeLineage"
190
+ );
191
+ assertRows(
192
+ "signal",
193
+ unwrapRuntimeEvidenceRows(runtime.signals, "signal"),
194
+ "runtimeLineage"
195
+ );
196
+ assertRows(
197
+ "evaluation",
198
+ unwrapRuntimeEvidenceRows(runtime.evaluations, "evaluation"),
199
+ "runtimeLineage"
200
+ );
201
+ assertRows(
202
+ "lineage scope",
203
+ unwrapRuntimeEvidenceRows(runtime.lineageScopes, "lineageScope"),
204
+ "lineage"
205
+ );
206
+ return deployment;
207
+ };
208
+
85
209
  // src/lib/runtimeScorecard.ts
86
210
  var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
87
211
  var asArray = (value) => Array.isArray(value) ? value : [];
@@ -120,75 +244,6 @@ var extractRuntimeRows = (artifact) => {
120
244
  statsBuckets: asArray(runtime.evaluationStatsBuckets).map(asRecord).filter((item) => item != null)
121
245
  };
122
246
  };
123
- var buildRuntimeLineageSummary = (rows) => {
124
- const lineageRows = [
125
- ...rows.evaluations,
126
- ...rows.signals,
127
- ...rows.trades,
128
- ...rows.lineageScopes.map((scope) => ({
129
- runtimeLineage: scope.lineage
130
- }))
131
- ];
132
- const lineages = lineageRows.map((row) => asRecord(row.runtimeLineage)).filter((lineage) => lineage != null);
133
- const identities = /* @__PURE__ */ new Map();
134
- for (const lineage of lineages) {
135
- const identity2 = lineage.schemaVersion === 3 ? {
136
- schemaVersion: 3,
137
- strategyRevision: finiteString(lineage.strategyRevision),
138
- deploymentCompositionId: finiteString(
139
- lineage.deploymentCompositionId
140
- ),
141
- strategyPackageVersion: finiteString(
142
- lineage.strategyPackageVersion
143
- ),
144
- strategyDependencyVersions: packageVersionMap(
145
- lineage.strategyDependencyVersions
146
- ),
147
- runtimePackageVersion: finiteString(lineage.runtimePackageVersion),
148
- maxLossValue: finiteNumber(lineage.maxLossValue)
149
- } : {
150
- schemaVersion: 1,
151
- compositionId: finiteString(lineage.compositionId),
152
- gitSha: finiteString(lineage.gitSha),
153
- gitDirty: typeof lineage.gitDirty === "boolean" ? lineage.gitDirty : null,
154
- gateFingerprint: finiteString(lineage.gateFingerprint),
155
- configFingerprint: finiteString(lineage.configFingerprint),
156
- contextFingerprint: finiteString(lineage.contextFingerprint),
157
- maxLossValue: finiteNumber(lineage.maxLossValue)
158
- };
159
- identities.set(JSON.stringify(identity2), identity2);
160
- }
161
- const identity = identities.size === 1 ? [...identities.values()][0] : null;
162
- const schemaVersion = finiteNumber(identity?.schemaVersion);
163
- const identityComplete = lineages.length > 0 && identity != null && (schemaVersion === 3 && finiteString(identity.strategyRevision) != null && finiteString(identity.deploymentCompositionId) != null && finiteString(identity.strategyPackageVersion) != null && packageVersionMap(identity.strategyDependencyVersions) != null && finiteString(identity.runtimePackageVersion) != null || schemaVersion === 1 && identity.compositionId != null && identity.gitSha != null && identity.gitDirty === false && identity.gateFingerprint != null && identity.configFingerprint != null && identity.contextFingerprint != null) && identity.maxLossValue != null;
164
- const coverageComplete = lineageRows.length > 0 && lineages.length === lineageRows.length;
165
- const complete = identityComplete && coverageComplete;
166
- const lineageKey = identityComplete ? runtimeLineageKey(identity) : null;
167
- return {
168
- complete,
169
- identityComplete,
170
- coverageComplete,
171
- conflicts: identities.size > 1,
172
- rows: lineageRows.length,
173
- rowsWithLineage: lineages.length,
174
- schemaVersion,
175
- lineageKey,
176
- strategyRevision: finiteString(identity?.strategyRevision),
177
- deploymentCompositionId: finiteString(identity?.deploymentCompositionId),
178
- strategyPackageVersion: finiteString(identity?.strategyPackageVersion),
179
- strategyDependencyVersions: packageVersionMap(
180
- identity?.strategyDependencyVersions
181
- ),
182
- runtimePackageVersion: finiteString(identity?.runtimePackageVersion),
183
- compositionId: finiteString(identity?.compositionId),
184
- gitSha: finiteString(identity?.gitSha),
185
- gitDirty: typeof identity?.gitDirty === "boolean" ? identity.gitDirty : null,
186
- gateFingerprint: finiteString(identity?.gateFingerprint),
187
- configFingerprint: finiteString(identity?.configFingerprint),
188
- contextFingerprint: finiteString(identity?.contextFingerprint),
189
- maxLossValue: finiteNumber(identity?.maxLossValue)
190
- };
191
- };
192
247
  var buildEmbeddedLineageScope = ({
193
248
  artifact,
194
249
  rows,
@@ -300,12 +355,6 @@ var buildEmbeddedLineageScope = ({
300
355
  strategyPackageVersion: singleTarget?.strategyPackageVersion ?? null,
301
356
  strategyDependencyVersions: singleTarget?.strategyDependencyVersions ?? null,
302
357
  runtimePackageVersion: singleTarget?.runtimePackageVersion ?? null,
303
- compositionId: null,
304
- gitSha: null,
305
- gitDirty: null,
306
- gateFingerprint: null,
307
- configFingerprint: null,
308
- contextFingerprint: null,
309
358
  maxLossValue: commonRiskValues.size === 1 ? [...commonRiskValues][0] : null,
310
359
  strategyScopes: Object.fromEntries(
311
360
  targetValues.map((target) => [
@@ -321,12 +370,6 @@ var buildEmbeddedLineageScope = ({
321
370
  }
322
371
  };
323
372
  };
324
- var belongsToRuntimeLineage = (row, summary) => {
325
- if (!summary.identityComplete || !summary.lineageKey) return false;
326
- const lineage = asRecord(row.runtimeLineage);
327
- if (!lineage) return false;
328
- return runtimeLineageKey(lineage) === summary.lineageKey && finiteNumber(lineage.maxLossValue) === summary.maxLossValue;
329
- };
330
373
  var runtimeDeploymentBinding = (artifact) => {
331
374
  const deployment = asRecord(asRecord(artifact)?.deployment);
332
375
  return {
@@ -508,10 +551,6 @@ var replayCountsForStrategy = (replayComparison, strategy) => {
508
551
  runtimeOnly: finiteNumber(counts?.runtimeOnly) ?? 0
509
552
  };
510
553
  };
511
- var getCalibrationSummary = (artifact) => {
512
- const root = asRecord(artifact) ?? {};
513
- return asRecord(asRecord(root.summary)?.all) ?? null;
514
- };
515
554
  var getProspectiveSummary = (artifact) => {
516
555
  const root = asRecord(artifact);
517
556
  if (!root || root.reportType !== "strategy-prospective-evidence") return null;
@@ -537,6 +576,10 @@ var buildRuntimeScorecard = ({
537
576
  strategy = null,
538
577
  llmComparatorPolicy = "ai_approved_only"
539
578
  }) => {
579
+ assertCurrentRuntimeEvidenceArtifact(runtimeArtifact);
580
+ for (const artifact of historyRuntimeArtifacts) {
581
+ assertCurrentRuntimeEvidenceArtifact(artifact);
582
+ }
540
583
  const scopedRuntimeArtifact = filterArtifactByStrategy(
541
584
  runtimeArtifact,
542
585
  strategy
@@ -551,7 +594,12 @@ var buildRuntimeScorecard = ({
551
594
  rows,
552
595
  strategy
553
596
  });
554
- const runtimeLineage = embeddedLineageScope?.summary ?? buildRuntimeLineageSummary(rows);
597
+ if (!embeddedLineageScope) {
598
+ throw new Error(
599
+ "Runtime scorecard requires the current embedded deployment lineage"
600
+ );
601
+ }
602
+ const runtimeLineage = embeddedLineageScope.summary;
555
603
  const window = runtimeWindow(runtimeArtifact);
556
604
  const startTime = finiteNumber(window.startTime) ?? generatedAt - 864e5;
557
605
  const endTime = finiteNumber(window.endTime) ?? generatedAt;
@@ -599,32 +647,26 @@ var buildRuntimeScorecard = ({
599
647
  const lineage = asRecord(replayComparison?.lineage);
600
648
  const lineageReason = finiteString(lineage?.reason);
601
649
  const calibrationRoot = asRecord(calibrationArtifact) ?? {};
602
- const calibrationSummary = asRecord(calibrationRoot.summary);
603
- const calibrationSamples = asArray(calibrationRoot.samples).map(asRecord).filter((item) => item != null).filter((item) => !strategy || finiteString(item.strategy) === strategy).filter(
604
- (item) => !embeddedLineageScope || embeddedLineageScope.belongs(item)
605
- );
650
+ const calibrationSamples = asArray(calibrationRoot.samples).map(asRecord).filter((item) => item != null).filter((item) => !strategy || finiteString(item.strategy) === strategy).filter((item) => embeddedLineageScope.belongs(item));
606
651
  const averageSampleMetric = (field) => {
607
652
  const values = calibrationSamples.map((sample) => finiteNumber(sample[field])).filter((value) => value != null);
608
653
  return values.length ? round(sum(values) / values.length) : null;
609
654
  };
610
- const fallbackCalibration = strategy ? asRecord(asRecord(calibrationSummary?.byStrategy)?.[strategy]) : getCalibrationSummary(calibrationArtifact);
611
- const actualSlippageBps = embeddedLineageScope ? averageSampleMetric("signalToFillAdverseBps") : finiteNumber(asRecord(fallbackCalibration?.signalToFillAdverseBps)?.avg);
612
- const residualVsModelBps = embeddedLineageScope ? averageSampleMetric("residualVsCurrentModelBps") : finiteNumber(
613
- asRecord(fallbackCalibration?.residualVsCurrentModelBps)?.avg
614
- );
655
+ const actualSlippageBps = averageSampleMetric("signalToFillAdverseBps");
656
+ const residualVsModelBps = averageSampleMetric("residualVsCurrentModelBps");
615
657
  const prospective = getProspectiveSummary(prospectiveEvidenceArtifact);
616
658
  const historyTrades = dedupeTrades([
617
659
  ...scopedHistoryArtifacts,
618
660
  scopedRuntimeArtifact
619
661
  ]);
620
662
  const comparableHistoryTrades = historyTrades.filter(
621
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
663
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && embeddedLineageScope.belongs(trade)
622
664
  );
623
665
  const comparableCurrentClosedTrades = currentClosedTrades.filter(
624
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
666
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && embeddedLineageScope.belongs(trade)
625
667
  );
626
668
  const comparableFills = rows.trades.filter(
627
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
669
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && embeddedLineageScope.belongs(trade)
628
670
  );
629
671
  const currentDistributions = buildDistributions(rows);
630
672
  const previousArtifact = scopedHistoryArtifacts.map((artifact) => ({ artifact, window: runtimeWindow(artifact) })).filter(({ window: candidateWindow }) => {
@@ -122,7 +122,7 @@ var buildStrategyLiveDiagnosisFromScorecard = ({
122
122
  );
123
123
  const orderFailureRate = scorecard.funnel.orderAttempts ? scorecard.funnel.orderFailures / scorecard.funnel.orderAttempts : null;
124
124
  const lineage = scorecard.lineage;
125
- const lineageComparable = scorecard.parity.lineageReason == null && lineage?.complete === true && lineage.conflicts === false && lineage.compositionId === manifest.composition.compositionId && lineage.gitDirty === false && lineage.gitSha === manifest.composition.gitSha && lineage.configFingerprint === manifest.composition.runtimeConfigFingerprint && lineage.gateFingerprint === manifest.composition.gateFingerprint && lineage.contextFingerprint === manifest.composition.runtimeContextFingerprint;
125
+ const lineageComparable = scorecard.parity.lineageReason == null && lineage?.complete === true && lineage.conflicts === false && lineage.schemaVersion === 3 && lineage.strategyRevision != null && lineage.deploymentCompositionId != null && lineage.strategyPackageVersion != null && lineage.strategyDependencyVersions != null && lineage.runtimePackageVersion != null;
126
126
  const releaseMaxLossValue = manifest.composition.maxLossValue;
127
127
  const runtimeMaxLossValue = lineage?.maxLossValue ?? null;
128
128
  const riskScaleComparable = Number.isFinite(releaseMaxLossValue) && releaseMaxLossValue > 0 && runtimeMaxLossValue != null && Number.isFinite(runtimeMaxLossValue) && runtimeMaxLossValue > 0;