@tradejs/cli 3.1.15-beta.228 → 3.1.15

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.
@@ -31,7 +31,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var runtimeEvidenceSync_exports = {};
32
32
  __export(runtimeEvidenceSync_exports, {
33
33
  buildRuntimeEvidenceRsyncArgs: () => buildRuntimeEvidenceRsyncArgs,
34
+ inspectRuntimeEvidenceBundleSupport: () => inspectRuntimeEvidenceBundleSupport,
34
35
  listPendingRuntimeEvidenceBundles: () => listPendingRuntimeEvidenceBundles,
36
+ listRuntimeEvidenceBundleStatuses: () => listRuntimeEvidenceBundleStatuses,
35
37
  markRuntimeEvidenceBundleProcessed: () => markRuntimeEvidenceBundleProcessed,
36
38
  syncRuntimeEvidenceBundles: () => syncRuntimeEvidenceBundles
37
39
  });
@@ -148,6 +150,70 @@ var discoverRuntimeEvidenceBundles = async (rootDir) => {
148
150
  return bundles.sort();
149
151
  };
150
152
 
153
+ // src/lib/runtimeEvidenceDeployment.ts
154
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
155
+ var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
156
+ var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
157
+ var isPackageVersionMap = (value) => isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(
158
+ ([name, version]) => name.startsWith("@tradejs/") && isNonEmptyString(version)
159
+ );
160
+ var INTERVALS = /* @__PURE__ */ new Set([
161
+ "1",
162
+ "3",
163
+ "5",
164
+ "15",
165
+ "30",
166
+ "60",
167
+ "120",
168
+ "240",
169
+ "360",
170
+ "720",
171
+ "D",
172
+ "W",
173
+ "M"
174
+ ]);
175
+ var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
176
+ 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));
177
+ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
178
+ 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)) {
179
+ throw new Error(
180
+ "Runtime evidence deployment snapshot is missing or invalid"
181
+ );
182
+ }
183
+ const strategies = value.strategies.map((candidate) => {
184
+ 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)) {
185
+ throw new Error(
186
+ "Runtime evidence deployment strategy snapshot is invalid"
187
+ );
188
+ }
189
+ if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
190
+ throw new Error(
191
+ `Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
192
+ );
193
+ }
194
+ if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
195
+ throw new Error(
196
+ `Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
197
+ );
198
+ }
199
+ return candidate;
200
+ });
201
+ const identities = /* @__PURE__ */ new Set();
202
+ for (const strategy of strategies) {
203
+ if (identities.has(strategy.strategyName)) {
204
+ throw new Error(
205
+ `Duplicate runtime evidence strategy: ${strategy.strategyName}`
206
+ );
207
+ }
208
+ identities.add(strategy.strategyName);
209
+ }
210
+ return {
211
+ ...value,
212
+ strategies
213
+ };
214
+ };
215
+ var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
216
+
151
217
  // src/lib/runtimeEvidenceSync.ts
152
218
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
153
219
  var isExistingDestinationError = (error) => {
@@ -180,7 +246,26 @@ var buildRuntimeEvidenceRsyncArgs = ({
180
246
  withTrailingSlash(destination)
181
247
  ];
182
248
  var receiptPath = (receiptsRoot, artifactId) => import_node_path2.default.join(receiptsRoot, `${safeSegment(artifactId, "artifact")}.json`);
183
- var listPendingRuntimeEvidenceBundles = async ({
249
+ var compareBundles = (left, right) => left.manifest.window.endTime - right.manifest.window.endTime || left.manifest.createdAt - right.manifest.createdAt;
250
+ var inspectRuntimeEvidenceBundleSupport = (bundle) => {
251
+ try {
252
+ const deployment = parseRuntimeEvidenceDeploymentSnapshot(
253
+ bundle.artifact.deployment
254
+ );
255
+ if (activeRuntimeEvidenceStrategies(deployment).length === 0) {
256
+ throw new Error(
257
+ "Runtime evidence deployment has no enabled active strategies"
258
+ );
259
+ }
260
+ return { supported: true };
261
+ } catch (error) {
262
+ return {
263
+ supported: false,
264
+ reason: error instanceof Error ? error.message : String(error)
265
+ };
266
+ }
267
+ };
268
+ var listRuntimeEvidenceBundleStatuses = async ({
184
269
  evidenceRoot,
185
270
  deploymentId
186
271
  }) => {
@@ -189,19 +274,39 @@ var listPendingRuntimeEvidenceBundles = async ({
189
274
  const receiptsRoot = import_node_path2.default.join(evidenceRoot, "receipts", safeDeploymentId);
190
275
  const bundles = await discoverRuntimeEvidenceBundles(artifactsRoot);
191
276
  const pending = [];
277
+ const unsupported = [];
192
278
  for (const bundleDir of bundles) {
193
279
  const verified = await verifyRuntimeEvidenceBundle(bundleDir);
194
280
  if (verified.manifest.deploymentId !== safeDeploymentId) continue;
195
281
  try {
196
282
  await import_promises2.default.access(receiptPath(receiptsRoot, verified.manifest.artifactId));
283
+ continue;
197
284
  } catch (error) {
198
285
  if (error.code !== "ENOENT") throw error;
286
+ }
287
+ const support = inspectRuntimeEvidenceBundleSupport(verified);
288
+ if (support.supported) {
199
289
  pending.push(verified);
290
+ } else {
291
+ unsupported.push({ bundle: verified, reason: support.reason });
200
292
  }
201
293
  }
202
- return pending.sort(
203
- (left, right) => left.manifest.window.startTime - right.manifest.window.startTime || left.manifest.createdAt - right.manifest.createdAt
204
- );
294
+ return {
295
+ pending: pending.sort(compareBundles),
296
+ unsupported: unsupported.sort(
297
+ (left, right) => compareBundles(left.bundle, right.bundle)
298
+ )
299
+ };
300
+ };
301
+ var listPendingRuntimeEvidenceBundles = async ({
302
+ evidenceRoot,
303
+ deploymentId
304
+ }) => {
305
+ const { pending } = await listRuntimeEvidenceBundleStatuses({
306
+ evidenceRoot,
307
+ deploymentId
308
+ });
309
+ return pending;
205
310
  };
206
311
  var markRuntimeEvidenceBundleProcessed = async ({
207
312
  evidenceRoot,
@@ -288,21 +393,24 @@ var syncRuntimeEvidenceBundles = async ({
288
393
  }
289
394
  received.push(await verifyRuntimeEvidenceBundle(destinationDir));
290
395
  }
396
+ const statuses = await listRuntimeEvidenceBundleStatuses({
397
+ evidenceRoot,
398
+ deploymentId: safeDeploymentId
399
+ });
291
400
  return {
292
401
  rsyncArgs,
293
402
  stdout: String(transfer.stdout ?? ""),
294
403
  stderr: String(transfer.stderr ?? ""),
295
404
  received,
296
- pending: await listPendingRuntimeEvidenceBundles({
297
- evidenceRoot,
298
- deploymentId: safeDeploymentId
299
- })
405
+ ...statuses
300
406
  };
301
407
  };
302
408
  // Annotate the CommonJS export names for ESM import in node:
303
409
  0 && (module.exports = {
304
410
  buildRuntimeEvidenceRsyncArgs,
411
+ inspectRuntimeEvidenceBundleSupport,
305
412
  listPendingRuntimeEvidenceBundles,
413
+ listRuntimeEvidenceBundleStatuses,
306
414
  markRuntimeEvidenceBundleProcessed,
307
415
  syncRuntimeEvidenceBundles
308
416
  });
@@ -189,6 +189,138 @@ var buildRuntimeLineageSummary = (rows) => {
189
189
  maxLossValue: finiteNumber(identity?.maxLossValue)
190
190
  };
191
191
  };
192
+ var buildEmbeddedLineageScope = ({
193
+ artifact,
194
+ rows,
195
+ strategy
196
+ }) => {
197
+ const deployment = asRecord(asRecord(artifact)?.deployment);
198
+ if (finiteNumber(deployment?.schemaVersion) !== 2) return null;
199
+ const deploymentId = finiteString(deployment?.id);
200
+ const accountId = finiteString(deployment?.accountId);
201
+ const deploymentCompositionId = finiteString(
202
+ deployment?.deploymentCompositionId
203
+ );
204
+ if (!deploymentId || !accountId || !deploymentCompositionId) return null;
205
+ const snapshots = asArray(deployment?.strategies).map(asRecord).filter((item) => item != null).filter((item) => item.enabled === true).filter(
206
+ (item) => !strategy || finiteString(item.strategyName) === strategy
207
+ );
208
+ const targetSeeds = snapshots.map((snapshot) => {
209
+ const strategyName = finiteString(snapshot.strategyName);
210
+ const strategyRevision = finiteString(snapshot.strategyRevision);
211
+ const strategyPackageVersion = finiteString(
212
+ snapshot.strategyPackageVersion
213
+ );
214
+ const strategyDependencyVersions = packageVersionMap(
215
+ snapshot.strategyDependencyVersions
216
+ );
217
+ const runtimePackageVersion = finiteString(
218
+ snapshot.runtimePackageVersion
219
+ );
220
+ return strategyName && strategyRevision && strategyPackageVersion && strategyDependencyVersions && runtimePackageVersion ? {
221
+ strategyName,
222
+ strategyRevision,
223
+ deploymentCompositionId,
224
+ strategyPackageVersion,
225
+ strategyDependencyVersions,
226
+ runtimePackageVersion
227
+ } : null;
228
+ }).filter((item) => item != null);
229
+ if (targetSeeds.length !== snapshots.length || targetSeeds.length === 0) {
230
+ return null;
231
+ }
232
+ const lineageRows = [
233
+ ...rows.evaluations,
234
+ ...rows.signals,
235
+ ...rows.trades,
236
+ ...rows.lineageScopes.map((scope) => ({
237
+ strategy: scope.strategy,
238
+ deploymentId: scope.deploymentId,
239
+ accountId: scope.accountId,
240
+ runtimeLineage: scope.lineage
241
+ }))
242
+ ];
243
+ const identityMatches = (lineage, target) => finiteNumber(lineage?.schemaVersion) === 3 && finiteString(lineage?.strategyRevision) === target.strategyRevision && finiteString(lineage?.deploymentCompositionId) === target.deploymentCompositionId && finiteString(lineage?.strategyPackageVersion) === target.strategyPackageVersion && JSON.stringify(packageVersionMap(lineage?.strategyDependencyVersions)) === JSON.stringify(target.strategyDependencyVersions) && finiteString(lineage?.runtimePackageVersion) === target.runtimePackageVersion;
244
+ const targets = /* @__PURE__ */ new Map();
245
+ for (const seed of targetSeeds) {
246
+ const matchingRows = lineageRows.filter(
247
+ (row) => finiteString(row.strategy) === seed.strategyName && finiteString(row.deploymentId) === deploymentId && finiteString(row.accountId) === accountId && identityMatches(asRecord(row.runtimeLineage), seed)
248
+ );
249
+ const riskValues = new Set(
250
+ matchingRows.map((row) => finiteNumber(asRecord(row.runtimeLineage)?.maxLossValue)).filter((value) => value != null)
251
+ );
252
+ targets.set(seed.strategyName, {
253
+ ...seed,
254
+ maxLossValue: riskValues.size === 1 ? [...riskValues][0] : null,
255
+ rowsWithLineage: matchingRows.length,
256
+ riskConflict: riskValues.size > 1
257
+ });
258
+ }
259
+ const belongs = (row) => {
260
+ const strategyName = finiteString(row.strategy);
261
+ const target = strategyName ? targets.get(strategyName) : null;
262
+ const lineage = asRecord(row.runtimeLineage);
263
+ return Boolean(
264
+ target && target.maxLossValue != null && identityMatches(lineage, target) && finiteNumber(lineage?.maxLossValue) === target.maxLossValue
265
+ );
266
+ };
267
+ const targetValues = [...targets.values()];
268
+ const coverageComplete = targetValues.every(
269
+ (target) => target.rowsWithLineage > 0 && target.maxLossValue != null
270
+ );
271
+ const commonRiskValues = new Set(
272
+ targetValues.map((target) => target.maxLossValue).filter((value) => value != null)
273
+ );
274
+ const singleTarget = targetValues.length === 1 ? targetValues[0] : null;
275
+ const singleLineage = singleTarget?.maxLossValue != null ? {
276
+ schemaVersion: 3,
277
+ strategyRevision: singleTarget.strategyRevision,
278
+ deploymentCompositionId: singleTarget.deploymentCompositionId,
279
+ strategyPackageVersion: singleTarget.strategyPackageVersion,
280
+ strategyDependencyVersions: singleTarget.strategyDependencyVersions,
281
+ runtimePackageVersion: singleTarget.runtimePackageVersion,
282
+ maxLossValue: singleTarget.maxLossValue
283
+ } : null;
284
+ return {
285
+ belongs,
286
+ summary: {
287
+ complete: coverageComplete,
288
+ identityComplete: true,
289
+ coverageComplete,
290
+ conflicts: targetValues.some((target) => target.riskConflict),
291
+ rows: lineageRows.length,
292
+ rowsWithLineage: targetValues.reduce(
293
+ (total, target) => total + target.rowsWithLineage,
294
+ 0
295
+ ),
296
+ schemaVersion: 3,
297
+ lineageKey: singleLineage ? runtimeLineageKey(singleLineage) : null,
298
+ strategyRevision: singleTarget?.strategyRevision ?? null,
299
+ deploymentCompositionId,
300
+ strategyPackageVersion: singleTarget?.strategyPackageVersion ?? null,
301
+ strategyDependencyVersions: singleTarget?.strategyDependencyVersions ?? null,
302
+ runtimePackageVersion: singleTarget?.runtimePackageVersion ?? null,
303
+ compositionId: null,
304
+ gitSha: null,
305
+ gitDirty: null,
306
+ gateFingerprint: null,
307
+ configFingerprint: null,
308
+ contextFingerprint: null,
309
+ maxLossValue: commonRiskValues.size === 1 ? [...commonRiskValues][0] : null,
310
+ strategyScopes: Object.fromEntries(
311
+ targetValues.map((target) => [
312
+ target.strategyName,
313
+ {
314
+ strategyRevision: target.strategyRevision,
315
+ rowsWithLineage: target.rowsWithLineage,
316
+ maxLossValue: target.maxLossValue,
317
+ complete: target.rowsWithLineage > 0 && target.maxLossValue != null
318
+ }
319
+ ])
320
+ )
321
+ }
322
+ };
323
+ };
192
324
  var belongsToRuntimeLineage = (row, summary) => {
193
325
  if (!summary.identityComplete || !summary.lineageKey) return false;
194
326
  const lineage = asRecord(row.runtimeLineage);
@@ -414,7 +546,12 @@ var buildRuntimeScorecard = ({
414
546
  );
415
547
  const deploymentBinding = runtimeDeploymentBinding(runtimeArtifact);
416
548
  const rows = extractRuntimeRows(scopedRuntimeArtifact);
417
- const runtimeLineage = buildRuntimeLineageSummary(rows);
549
+ const embeddedLineageScope = buildEmbeddedLineageScope({
550
+ artifact: runtimeArtifact,
551
+ rows,
552
+ strategy
553
+ });
554
+ const runtimeLineage = embeddedLineageScope?.summary ?? buildRuntimeLineageSummary(rows);
418
555
  const window = runtimeWindow(runtimeArtifact);
419
556
  const startTime = finiteNumber(window.startTime) ?? generatedAt - 864e5;
420
557
  const endTime = finiteNumber(window.endTime) ?? generatedAt;
@@ -463,12 +600,17 @@ var buildRuntimeScorecard = ({
463
600
  const lineageReason = finiteString(lineage?.reason);
464
601
  const calibrationRoot = asRecord(calibrationArtifact) ?? {};
465
602
  const calibrationSummary = asRecord(calibrationRoot.summary);
466
- const calibration = strategy ? asRecord(asRecord(calibrationSummary?.byStrategy)?.[strategy]) : getCalibrationSummary(calibrationArtifact);
467
- const actualSlippageBps = finiteNumber(
468
- asRecord(calibration?.signalToFillAdverseBps)?.avg
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)
469
605
  );
470
- const residualVsModelBps = finiteNumber(
471
- asRecord(calibration?.residualVsCurrentModelBps)?.avg
606
+ const averageSampleMetric = (field) => {
607
+ const values = calibrationSamples.map((sample) => finiteNumber(sample[field])).filter((value) => value != null);
608
+ return values.length ? round(sum(values) / values.length) : null;
609
+ };
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
472
614
  );
473
615
  const prospective = getProspectiveSummary(prospectiveEvidenceArtifact);
474
616
  const historyTrades = dedupeTrades([
@@ -476,10 +618,13 @@ var buildRuntimeScorecard = ({
476
618
  scopedRuntimeArtifact
477
619
  ]);
478
620
  const comparableHistoryTrades = historyTrades.filter(
479
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && belongsToRuntimeLineage(trade, runtimeLineage)
621
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
480
622
  );
481
623
  const comparableCurrentClosedTrades = currentClosedTrades.filter(
482
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && belongsToRuntimeLineage(trade, runtimeLineage)
624
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
625
+ );
626
+ const comparableFills = rows.trades.filter(
627
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
483
628
  );
484
629
  const currentDistributions = buildDistributions(rows);
485
630
  const previousArtifact = scopedHistoryArtifacts.map((artifact) => ({ artifact, window: runtimeWindow(artifact) })).filter(({ window: candidateWindow }) => {
@@ -593,7 +738,8 @@ var buildRuntimeScorecard = ({
593
738
  `${finiteString(signal.orderSkipReason) ?? ""} ${finiteString(signal.orderFailureReason) ?? ""}`
594
739
  )
595
740
  ).length,
596
- fills: rows.trades.length,
741
+ fills: comparableFills.length,
742
+ nonComparableFills: rows.trades.length - comparableFills.length,
597
743
  closedTrades: currentClosedTrades.length,
598
744
  comparableClosedTrades: comparableCurrentClosedTrades.length,
599
745
  nonComparableClosedTrades: currentClosedTrades.length - comparableCurrentClosedTrades.length
@@ -642,7 +788,7 @@ var buildRuntimeScorecard = ({
642
788
  lineageReason
643
789
  },
644
790
  execution: {
645
- available: calibration != null,
791
+ available: actualSlippageBps != null || residualVsModelBps != null,
646
792
  actualSignalToFillSlippageBps: actualSlippageBps,
647
793
  residualVsCurrentModelBps: residualVsModelBps
648
794
  },
@@ -4706,6 +4706,24 @@ var INTERVALS = /* @__PURE__ */ new Set([
4706
4706
  ]);
4707
4707
  var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
4708
4708
  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));
4709
+ var samePackageVersions = (left, right) => {
4710
+ if (!left || !right) return false;
4711
+ const leftEntries = Object.entries(left).sort(
4712
+ ([a], [b]) => a.localeCompare(b)
4713
+ );
4714
+ const rightEntries = Object.entries(right).sort(
4715
+ ([a], [b]) => a.localeCompare(b)
4716
+ );
4717
+ return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
4718
+ };
4719
+ var lineageMatchesStrategySnapshot = ({
4720
+ lineage,
4721
+ deployment,
4722
+ strategy
4723
+ }) => lineage?.schemaVersion === 3 && lineage.strategyRevision === strategy.strategyRevision && lineage.deploymentCompositionId === deployment.deploymentCompositionId && lineage.strategyPackageVersion === strategy.strategyPackageVersion && lineage.runtimePackageVersion === strategy.runtimePackageVersion && samePackageVersions(
4724
+ lineage.strategyDependencyVersions,
4725
+ strategy.strategyDependencyVersions
4726
+ );
4709
4727
  var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4710
4728
  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)) {
4711
4729
  throw new Error(
@@ -4745,6 +4763,54 @@ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4745
4763
  };
4746
4764
  };
4747
4765
  var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
4766
+ var resolveRuntimeEvidenceTickerUniverse = ({
4767
+ deployment,
4768
+ lineageScopes
4769
+ }) => {
4770
+ const declaredTickers = deployment.tickers?.filter(isNonEmptyString) ?? [];
4771
+ if (declaredTickers.length > 0) {
4772
+ return {
4773
+ ...deployment,
4774
+ tickers: [
4775
+ ...new Set(declaredTickers.map((ticker) => ticker.trim()))
4776
+ ].sort()
4777
+ };
4778
+ }
4779
+ const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
4780
+ if (activeStrategies.length > 0 && activeStrategies.every((strategy) => strategy.selection?.tickers?.length)) {
4781
+ return {
4782
+ ...deployment,
4783
+ tickers: [
4784
+ ...new Set(
4785
+ activeStrategies.flatMap(
4786
+ (strategy) => strategy.selection?.tickers ?? []
4787
+ )
4788
+ )
4789
+ ].sort()
4790
+ };
4791
+ }
4792
+ const strategiesByName = new Map(
4793
+ activeStrategies.map((strategy) => [strategy.strategyName, strategy])
4794
+ );
4795
+ const tickers = /* @__PURE__ */ new Set();
4796
+ for (const scope of lineageScopes) {
4797
+ const strategy = strategiesByName.get(scope.strategy);
4798
+ if (!strategy || scope.deploymentId !== deployment.id || scope.accountId !== deployment.accountId || !isNonEmptyString(scope.symbol) || !lineageMatchesStrategySnapshot({
4799
+ lineage: scope.lineage,
4800
+ deployment,
4801
+ strategy
4802
+ })) {
4803
+ continue;
4804
+ }
4805
+ tickers.add(scope.symbol.trim());
4806
+ }
4807
+ if (tickers.size === 0) {
4808
+ throw new Error(
4809
+ "Runtime evidence has no immutable ticker universe for the embedded deployment composition"
4810
+ );
4811
+ }
4812
+ return { ...deployment, tickers: [...tickers].sort() };
4813
+ };
4748
4814
  var runtimeDeploymentFromEvidence = (deployment) => ({
4749
4815
  id: deployment.id,
4750
4816
  deploymentCompositionId: deployment.deploymentCompositionId,
@@ -4812,7 +4878,13 @@ var readRuntimeEvidenceArtifact = async ({
4812
4878
  userName: String(root.userName ?? runtime.userName ?? "").trim(),
4813
4879
  startTime: asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow?.startTime),
4814
4880
  endTime: asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow?.endTime),
4815
- deployment: parseRuntimeEvidenceDeploymentSnapshot(root.deployment)
4881
+ deployment: resolveRuntimeEvidenceTickerUniverse({
4882
+ deployment: parseRuntimeEvidenceDeploymentSnapshot(root.deployment),
4883
+ lineageScopes: unwrapRows(
4884
+ runtime.lineageScopes,
4885
+ "lineageScope"
4886
+ )
4887
+ })
4816
4888
  };
4817
4889
  };
4818
4890
  var loadReplayRuntimeEvidenceMetadata = async (params) => {
@@ -7735,7 +7807,7 @@ var filterReplayComparisonByLineage = ({
7735
7807
  excludedRuntimeLineageScopes: runtimeLineageScopes.length - comparableRuntimeLineageScopes.length,
7736
7808
  excludedExchangeEntries: 0,
7737
7809
  excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
7738
- reason: comparableScopeKeys.size > 0 ? null : "no_runtime_artifacts_with_matching_lineage",
7810
+ reason: comparableScopeKeys.size > 0 ? null : expectedByScope.size === 0 ? "replay_produced_no_lineage_scopes" : "no_runtime_artifacts_with_matching_lineage",
7739
7811
  replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
7740
7812
  const [deploymentId, accountId, strategy, symbol] = scope.split("::");
7741
7813
  return {
@@ -4709,6 +4709,24 @@ var INTERVALS = /* @__PURE__ */ new Set([
4709
4709
  ]);
4710
4710
  var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
4711
4711
  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));
4712
+ var samePackageVersions = (left, right) => {
4713
+ if (!left || !right) return false;
4714
+ const leftEntries = Object.entries(left).sort(
4715
+ ([a], [b]) => a.localeCompare(b)
4716
+ );
4717
+ const rightEntries = Object.entries(right).sort(
4718
+ ([a], [b]) => a.localeCompare(b)
4719
+ );
4720
+ return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
4721
+ };
4722
+ var lineageMatchesStrategySnapshot = ({
4723
+ lineage,
4724
+ deployment,
4725
+ strategy
4726
+ }) => lineage?.schemaVersion === 3 && lineage.strategyRevision === strategy.strategyRevision && lineage.deploymentCompositionId === deployment.deploymentCompositionId && lineage.strategyPackageVersion === strategy.strategyPackageVersion && lineage.runtimePackageVersion === strategy.runtimePackageVersion && samePackageVersions(
4727
+ lineage.strategyDependencyVersions,
4728
+ strategy.strategyDependencyVersions
4729
+ );
4712
4730
  var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4713
4731
  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)) {
4714
4732
  throw new Error(
@@ -4748,6 +4766,54 @@ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4748
4766
  };
4749
4767
  };
4750
4768
  var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
4769
+ var resolveRuntimeEvidenceTickerUniverse = ({
4770
+ deployment,
4771
+ lineageScopes
4772
+ }) => {
4773
+ const declaredTickers = deployment.tickers?.filter(isNonEmptyString) ?? [];
4774
+ if (declaredTickers.length > 0) {
4775
+ return {
4776
+ ...deployment,
4777
+ tickers: [
4778
+ ...new Set(declaredTickers.map((ticker) => ticker.trim()))
4779
+ ].sort()
4780
+ };
4781
+ }
4782
+ const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
4783
+ if (activeStrategies.length > 0 && activeStrategies.every((strategy) => strategy.selection?.tickers?.length)) {
4784
+ return {
4785
+ ...deployment,
4786
+ tickers: [
4787
+ ...new Set(
4788
+ activeStrategies.flatMap(
4789
+ (strategy) => strategy.selection?.tickers ?? []
4790
+ )
4791
+ )
4792
+ ].sort()
4793
+ };
4794
+ }
4795
+ const strategiesByName = new Map(
4796
+ activeStrategies.map((strategy) => [strategy.strategyName, strategy])
4797
+ );
4798
+ const tickers = /* @__PURE__ */ new Set();
4799
+ for (const scope of lineageScopes) {
4800
+ const strategy = strategiesByName.get(scope.strategy);
4801
+ if (!strategy || scope.deploymentId !== deployment.id || scope.accountId !== deployment.accountId || !isNonEmptyString(scope.symbol) || !lineageMatchesStrategySnapshot({
4802
+ lineage: scope.lineage,
4803
+ deployment,
4804
+ strategy
4805
+ })) {
4806
+ continue;
4807
+ }
4808
+ tickers.add(scope.symbol.trim());
4809
+ }
4810
+ if (tickers.size === 0) {
4811
+ throw new Error(
4812
+ "Runtime evidence has no immutable ticker universe for the embedded deployment composition"
4813
+ );
4814
+ }
4815
+ return { ...deployment, tickers: [...tickers].sort() };
4816
+ };
4751
4817
  var runtimeDeploymentFromEvidence = (deployment) => ({
4752
4818
  id: deployment.id,
4753
4819
  deploymentCompositionId: deployment.deploymentCompositionId,
@@ -4815,7 +4881,13 @@ var readRuntimeEvidenceArtifact = async ({
4815
4881
  userName: String(root.userName ?? runtime.userName ?? "").trim(),
4816
4882
  startTime: asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow?.startTime),
4817
4883
  endTime: asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow?.endTime),
4818
- deployment: parseRuntimeEvidenceDeploymentSnapshot(root.deployment)
4884
+ deployment: resolveRuntimeEvidenceTickerUniverse({
4885
+ deployment: parseRuntimeEvidenceDeploymentSnapshot(root.deployment),
4886
+ lineageScopes: unwrapRows(
4887
+ runtime.lineageScopes,
4888
+ "lineageScope"
4889
+ )
4890
+ })
4819
4891
  };
4820
4892
  };
4821
4893
  var loadReplayRuntimeEvidenceMetadata = async (params) => {
@@ -7738,7 +7810,7 @@ var filterReplayComparisonByLineage = ({
7738
7810
  excludedRuntimeLineageScopes: runtimeLineageScopes.length - comparableRuntimeLineageScopes.length,
7739
7811
  excludedExchangeEntries: 0,
7740
7812
  excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
7741
- reason: comparableScopeKeys.size > 0 ? null : "no_runtime_artifacts_with_matching_lineage",
7813
+ reason: comparableScopeKeys.size > 0 ? null : expectedByScope.size === 0 ? "replay_produced_no_lineage_scopes" : "no_runtime_artifacts_with_matching_lineage",
7742
7814
  replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
7743
7815
  const [deploymentId, accountId, strategy, symbol] = scope.split("::");
7744
7816
  return {