@tradejs/cli 3.1.14 → 3.1.15-beta.228

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.
package/dist/cli.js CHANGED
@@ -27242,7 +27242,7 @@ async function collectReleaseEvidenceReferences(references) {
27242
27242
  })
27243
27243
  );
27244
27244
  }
27245
- var import_node_crypto12, import_promises26, import_node_path21, import_types10, import_strategyReleaseEvidence3, SHA256_RE2, LINEAGE_FINGERPRINT_RE, STRATEGY_REVISION_RE, DEPLOYMENT_COMPOSITION_ID_RE, REQUIRED_LONG_WINDOWS, REQUIRED_STABLE_TERMINAL_WINDOWS, MIN_RECENT_DIRECTION_REPAIR_TRADES, safeSegment3, compactTimestamp2, verifyFullPeriodChartArtifact, hasExactRuntimeTarget, deriveStrategyReleaseResearchDecision, canonicalStrategyReleaseJson, strategyReleaseSha256, assertFingerprint, assertLineageFingerprint, releaseReasons, deriveReleaseGates, verifyEvidenceComposition, createStrategyReleaseManifest, createStrategyReleaseEnvelope, verifyStrategyReleaseEnvelope, verifyStrategyEvidenceMarkerEnvelope, releaseMarkers, writeJsonAtomic2, publishStrategyRelease, publishStrategyLiveDiagnosis, finiteString3, finiteNumber6, finiteDirectionPolicy, uniqueStrings, incompleteEvidenceLineage, extractEvidenceLineage, verifyCompletedCoreResearchBundle;
27245
+ var import_node_crypto12, import_promises26, import_node_path21, import_types10, import_strategyReleaseEvidence3, SHA256_RE2, LINEAGE_FINGERPRINT_RE, STRATEGY_REVISION_RE, DEPLOYMENT_COMPOSITION_ID_RE, REQUIRED_LONG_WINDOWS, REQUIRED_STABLE_TERMINAL_WINDOWS, MIN_RECENT_DIRECTION_REPAIR_EVENTS, MIN_SELECTION_GRADE_TERMINAL_EVENTS, safeSegment3, compactTimestamp2, verifyFullPeriodChartArtifact, readVerifiedJsonArtifact, verifyResearchDecisionLineage, hasExactRuntimeTarget, deriveStrategyReleaseResearchDecision, canonicalStrategyReleaseJson, strategyReleaseSha256, assertFingerprint, assertLineageFingerprint, releaseReasons, deriveReleaseGates, verifyEvidenceComposition, createStrategyReleaseManifest, createStrategyReleaseEnvelope, verifyStrategyReleaseEnvelope, verifyStrategyEvidenceMarkerEnvelope, releaseMarkers, writeJsonAtomic2, publishStrategyRelease, publishStrategyLiveDiagnosis, finiteString3, finiteNumber6, finiteDirectionPolicy, uniqueStrings, incompleteEvidenceLineage, extractEvidenceLineage, verifyCompletedCoreResearchBundle;
27246
27246
  var init_strategyRelease = __esm({
27247
27247
  "src/lib/strategyRelease.ts"() {
27248
27248
  "use strict";
@@ -27260,7 +27260,8 @@ var init_strategyRelease = __esm({
27260
27260
  DEPLOYMENT_COMPOSITION_ID_RE = /^dc1:[a-f0-9]{16}$/;
27261
27261
  REQUIRED_LONG_WINDOWS = [1095, 1460, 1825];
27262
27262
  REQUIRED_STABLE_TERMINAL_WINDOWS = [365, 180, 90];
27263
- MIN_RECENT_DIRECTION_REPAIR_TRADES = 20;
27263
+ MIN_RECENT_DIRECTION_REPAIR_EVENTS = 20;
27264
+ MIN_SELECTION_GRADE_TERMINAL_EVENTS = 50;
27264
27265
  safeSegment3 = import_strategyReleaseEvidence3.safeStrategyEvidenceSegment;
27265
27266
  compactTimestamp2 = import_strategyReleaseEvidence3.compactStrategyEvidenceTimestamp;
27266
27267
  verifyFullPeriodChartArtifact = async (input) => {
@@ -27279,16 +27280,51 @@ var init_strategyRelease = __esm({
27279
27280
  return false;
27280
27281
  }
27281
27282
  };
27283
+ readVerifiedJsonArtifact = async (artifact) => {
27284
+ if (!artifact?.path || !SHA256_RE2.test(artifact.sha256)) return null;
27285
+ try {
27286
+ const artifactPath = import_node_path21.default.resolve(artifact.path);
27287
+ if (await (0, import_strategyReleaseEvidence3.strategyEvidenceFileSha256)(artifactPath) !== artifact.sha256) {
27288
+ return null;
27289
+ }
27290
+ return JSON.parse(await import_promises26.default.readFile(artifactPath, "utf8"));
27291
+ } catch {
27292
+ return null;
27293
+ }
27294
+ };
27295
+ verifyResearchDecisionLineage = async (input) => {
27296
+ const progress = await readVerifiedJsonArtifact(input.progressArtifact);
27297
+ const selected = await readVerifiedJsonArtifact(
27298
+ input.selectedCompositionArtifact
27299
+ );
27300
+ if (!progress || !selected) return false;
27301
+ const progressSelection = progress.selectedComposition;
27302
+ return Boolean(
27303
+ progress.schema === "tradejs-release-progress/v2" && progress.strategy === input.strategy && progress.status === "complete" && progress.verdictAllowed === true && typeof progress.lineageId === "string" && progress.lineageId.length > 0 && typeof progress.objectiveFingerprint === "string" && SHA256_RE2.test(progress.objectiveFingerprint) && typeof progressSelection?.candidateId === "string" && progressSelection.candidateId.length > 0 && typeof progressSelection.compositionFingerprint === "string" && SHA256_RE2.test(progressSelection.compositionFingerprint) && progressSelection.artifactSha256 === input.selectedCompositionArtifact?.sha256 && selected.schema === "tradejs-release-selected-composition/v2" && selected.strategy === input.strategy && selected.lineageId === progress.lineageId && selected.objectiveFingerprint === progress.objectiveFingerprint && selected.candidateId === progressSelection.candidateId && selected.compositionFingerprint === progressSelection.compositionFingerprint && selected.chartSha256 === input.chartArtifact?.sha256
27304
+ );
27305
+ };
27282
27306
  hasExactRuntimeTarget = (target, strategyName) => Boolean(
27283
27307
  target?.userName.trim() && target.deploymentId.trim() && target.accountId.trim() && target.strategyName.trim() === strategyName.trim() && STRATEGY_REVISION_RE.test(target.strategyRevision) && DEPLOYMENT_COMPOSITION_ID_RE.test(target.deploymentCompositionId)
27284
27308
  );
27285
27309
  deriveStrategyReleaseResearchDecision = async (input) => {
27286
27310
  const blockers = [];
27311
+ if (!await verifyResearchDecisionLineage(input)) {
27312
+ return {
27313
+ strategy: input.strategy,
27314
+ action: "FORWARD_BLOCKED",
27315
+ repairAllowed: false,
27316
+ targetDirection: input.recentFailure?.direction ?? null,
27317
+ maxLossValue: null,
27318
+ requiresRuntimeBinding: false,
27319
+ blockers: ["RESEARCH_LINEAGE_MISMATCH"],
27320
+ summary: "The progress decision, selected composition, objective, and chart do not resolve to one immutable research lineage."
27321
+ };
27322
+ }
27287
27323
  const directionPolicy = input.directionPolicy ?? "both";
27288
27324
  const activeDirections = directionPolicy === "long_only" ? ["long"] : directionPolicy === "short_only" ? ["short"] : ["long", "short"];
27289
27325
  const suppressedDirection = directionPolicy === "long_only" ? "short" : directionPolicy === "short_only" ? "long" : null;
27290
- const hasHistoricalEdge = (entry) => entry.pnl > 0 && entry.profitFactor >= 1 && activeDirections.every(
27291
- (direction) => entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
27326
+ const hasHistoricalEdge = (entry, supportConditioned = false) => (supportConditioned ? (entry.independentEvents ?? entry.trades ?? 0) < MIN_SELECTION_GRADE_TERMINAL_EVENTS || entry.pnl > 0 && entry.profitFactor >= 1 : entry.pnl > 0 && entry.profitFactor >= 1) && activeDirections.every(
27327
+ (direction) => supportConditioned && (entry[direction].independentEvents ?? entry[direction].trades ?? 0) < MIN_SELECTION_GRADE_TERMINAL_EVENTS || entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
27292
27328
  ) && (suppressedDirection == null || entry[suppressedDirection].trades === 0 && entry[suppressedDirection].pnl === 0);
27293
27329
  const byDays = new Map(
27294
27330
  input.historicalWindows.map((entry) => [entry.days, entry])
@@ -27308,7 +27344,16 @@ var init_strategyRelease = __esm({
27308
27344
  const stableTerminalWindows = REQUIRED_STABLE_TERMINAL_WINDOWS.map(
27309
27345
  (days) => byDays.get(days)
27310
27346
  ).filter((entry) => Boolean(entry));
27311
- if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry))) {
27347
+ if (stableTerminalWindows.some(
27348
+ (entry) => !Number.isFinite(entry.independentEvents ?? entry.trades) || activeDirections.some(
27349
+ (direction) => !Number.isFinite(
27350
+ entry[direction].independentEvents ?? entry[direction].trades
27351
+ )
27352
+ )
27353
+ )) {
27354
+ blockers.push("HISTORICAL_MATRIX_INCOMPLETE");
27355
+ }
27356
+ if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry, true))) {
27312
27357
  blockers.push("HISTORICAL_EDGE_FAILED");
27313
27358
  }
27314
27359
  if (blockers.length) {
@@ -27320,11 +27365,11 @@ var init_strategyRelease = __esm({
27320
27365
  maxLossValue: null,
27321
27366
  requiresRuntimeBinding: false,
27322
27367
  blockers,
27323
- summary: `The final ${directionPolicy} composition does not have complete positive evidence for ALL and every active direction on the frozen 3y/4y/max-window matrix.`
27368
+ summary: `The final ${directionPolicy} composition fails the frozen long-window edge or a selection-grade terminal guardrail. Underpowered and diagnostic terminal cohorts do not cause this stop.`
27324
27369
  };
27325
27370
  }
27326
27371
  const repairAllowed = Boolean(
27327
- input.recentFailure && input.recentFailure.closedTrades >= MIN_RECENT_DIRECTION_REPAIR_TRADES && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
27372
+ input.recentFailure && input.recentFailure.independentEvents >= MIN_RECENT_DIRECTION_REPAIR_EVENTS && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
27328
27373
  );
27329
27374
  if (repairAllowed) {
27330
27375
  return {
@@ -27335,7 +27380,7 @@ var init_strategyRelease = __esm({
27335
27380
  maxLossValue: null,
27336
27381
  requiresRuntimeBinding: false,
27337
27382
  blockers: [],
27338
- summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism, then rebuild the full evidence matrix."
27383
+ summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism with diagnostic independent support, then rebuild the full evidence matrix."
27339
27384
  };
27340
27385
  }
27341
27386
  if (!input.candidateImplemented) blockers.push("CANDIDATE_NOT_IMPLEMENTED");
@@ -30250,8 +30295,6 @@ var init_runtimeParity2 = __esm({
30250
30295
  buildReplayAiAnalyses = ({
30251
30296
  runtimeTrades,
30252
30297
  runtimeSignalEvaluations,
30253
- strategy,
30254
- symbol,
30255
30298
  toleranceMs
30256
30299
  }) => {
30257
30300
  const snapshots = [
@@ -30954,7 +30997,7 @@ var init_runtimeParity2 = __esm({
30954
30997
  runtimeEntriesCount,
30955
30998
  runtimeDuplicateEntriesCount,
30956
30999
  backtestEntriesCount,
30957
- matchedCount,
31000
+ matchedCount: _matchedCount,
30958
31001
  runtimeOnlyCount,
30959
31002
  backtestOnlyCount,
30960
31003
  matchedSummary,
@@ -31078,7 +31121,7 @@ var init_runtimeParity2 = __esm({
31078
31121
  window,
31079
31122
  connectorName,
31080
31123
  replayEnv,
31081
- runtimeGatesEnabled,
31124
+ runtimeGatesEnabled: _runtimeGatesEnabled,
31082
31125
  userName: userName3
31083
31126
  }) => [
31084
31127
  "\u{1F9EA} <b>TradeJS runtime parity</b>",
@@ -33784,7 +33827,7 @@ var init_portfolioReplayConnector = __esm({
33784
33827
  });
33785
33828
  };
33786
33829
  const getExecutionSlippageBreakdown = ({
33787
- stage,
33830
+ stage: _stage,
33788
33831
  signal
33789
33832
  }) => (0, import_trade2.calculateExecutionSlippageBreakdown)({
33790
33833
  spreadBps: (0, import_trade2.extractExecutionSpreadBps)(signal),
@@ -34296,7 +34339,6 @@ var init_historicalSignalsReplay = __esm({
34296
34339
  });
34297
34340
  const cycleSymbolsByTimestamp = /* @__PURE__ */ new Map();
34298
34341
  const sharedReplayKeyPrefixes = [];
34299
- let preparedSymbols = 0;
34300
34342
  let skippedSymbols = 0;
34301
34343
  const prepareBar = new import_progress12.default(
34302
34344
  "prepare :current/:total [:bar][:percent] skipped=:skipped :etas(s) :symbol",
@@ -34415,7 +34457,6 @@ var init_historicalSignalsReplay = __esm({
34415
34457
  currentIndex: 0,
34416
34458
  strategies: strategiesForSymbol
34417
34459
  };
34418
- preparedSymbols += 1;
34419
34460
  prepareBar.tick(1, {
34420
34461
  skipped: import_chalk46.default.yellow(skippedSymbols),
34421
34462
  symbol: import_chalk46.default.gray(symbol)
@@ -34445,7 +34486,7 @@ var init_historicalSignalsReplay = __esm({
34445
34486
  }
34446
34487
  );
34447
34488
  try {
34448
- for (const [cycleIndex, timestamp] of orderedTimestamps.entries()) {
34489
+ for (const timestamp of orderedTimestamps) {
34449
34490
  const cycleStartedAt = Date.now();
34450
34491
  const cycleSymbols = cycleSymbolsByTimestamp.get(timestamp) ?? [];
34451
34492
  for (const symbolRuntime of cycleSymbols) {
@@ -36425,9 +36466,6 @@ var init_runtimeComparison = __esm({
36425
36466
  toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
36426
36467
  signalTimestampOffsetMs: getReplayEntryTimestampCompareOffsetMs()
36427
36468
  });
36428
- const liveSummaryByStrategy = new Map(
36429
- liveStrategySummaries.map((summary) => [summary.strategyName, summary])
36430
- );
36431
36469
  const rowByStrategy = /* @__PURE__ */ new Map();
36432
36470
  const ensureRow = (strategyName) => {
36433
36471
  const existing = rowByStrategy.get(strategyName);
@@ -283,7 +283,8 @@ var STRATEGY_REVISION_RE = /^sr1:[a-f0-9]{16}$/;
283
283
  var DEPLOYMENT_COMPOSITION_ID_RE = /^dc1:[a-f0-9]{16}$/;
284
284
  var REQUIRED_LONG_WINDOWS = [1095, 1460, 1825];
285
285
  var REQUIRED_STABLE_TERMINAL_WINDOWS = [365, 180, 90];
286
- var MIN_RECENT_DIRECTION_REPAIR_TRADES = 20;
286
+ var MIN_RECENT_DIRECTION_REPAIR_EVENTS = 20;
287
+ var MIN_SELECTION_GRADE_TERMINAL_EVENTS = 50;
287
288
  var safeSegment = import_strategyReleaseEvidence.safeStrategyEvidenceSegment;
288
289
  var compactTimestamp = import_strategyReleaseEvidence.compactStrategyEvidenceTimestamp;
289
290
  var verifyFullPeriodChartArtifact = async (input) => {
@@ -302,16 +303,51 @@ var verifyFullPeriodChartArtifact = async (input) => {
302
303
  return false;
303
304
  }
304
305
  };
306
+ var readVerifiedJsonArtifact = async (artifact) => {
307
+ if (!artifact?.path || !SHA256_RE.test(artifact.sha256)) return null;
308
+ try {
309
+ const artifactPath = import_node_path.default.resolve(artifact.path);
310
+ if (await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(artifactPath) !== artifact.sha256) {
311
+ return null;
312
+ }
313
+ return JSON.parse(await import_promises.default.readFile(artifactPath, "utf8"));
314
+ } catch {
315
+ return null;
316
+ }
317
+ };
318
+ var verifyResearchDecisionLineage = async (input) => {
319
+ const progress = await readVerifiedJsonArtifact(input.progressArtifact);
320
+ const selected = await readVerifiedJsonArtifact(
321
+ input.selectedCompositionArtifact
322
+ );
323
+ if (!progress || !selected) return false;
324
+ const progressSelection = progress.selectedComposition;
325
+ return Boolean(
326
+ progress.schema === "tradejs-release-progress/v2" && progress.strategy === input.strategy && progress.status === "complete" && progress.verdictAllowed === true && typeof progress.lineageId === "string" && progress.lineageId.length > 0 && typeof progress.objectiveFingerprint === "string" && SHA256_RE.test(progress.objectiveFingerprint) && typeof progressSelection?.candidateId === "string" && progressSelection.candidateId.length > 0 && typeof progressSelection.compositionFingerprint === "string" && SHA256_RE.test(progressSelection.compositionFingerprint) && progressSelection.artifactSha256 === input.selectedCompositionArtifact?.sha256 && selected.schema === "tradejs-release-selected-composition/v2" && selected.strategy === input.strategy && selected.lineageId === progress.lineageId && selected.objectiveFingerprint === progress.objectiveFingerprint && selected.candidateId === progressSelection.candidateId && selected.compositionFingerprint === progressSelection.compositionFingerprint && selected.chartSha256 === input.chartArtifact?.sha256
327
+ );
328
+ };
305
329
  var hasExactRuntimeTarget = (target, strategyName) => Boolean(
306
330
  target?.userName.trim() && target.deploymentId.trim() && target.accountId.trim() && target.strategyName.trim() === strategyName.trim() && STRATEGY_REVISION_RE.test(target.strategyRevision) && DEPLOYMENT_COMPOSITION_ID_RE.test(target.deploymentCompositionId)
307
331
  );
308
332
  var deriveStrategyReleaseResearchDecision = async (input) => {
309
333
  const blockers = [];
334
+ if (!await verifyResearchDecisionLineage(input)) {
335
+ return {
336
+ strategy: input.strategy,
337
+ action: "FORWARD_BLOCKED",
338
+ repairAllowed: false,
339
+ targetDirection: input.recentFailure?.direction ?? null,
340
+ maxLossValue: null,
341
+ requiresRuntimeBinding: false,
342
+ blockers: ["RESEARCH_LINEAGE_MISMATCH"],
343
+ summary: "The progress decision, selected composition, objective, and chart do not resolve to one immutable research lineage."
344
+ };
345
+ }
310
346
  const directionPolicy = input.directionPolicy ?? "both";
311
347
  const activeDirections = directionPolicy === "long_only" ? ["long"] : directionPolicy === "short_only" ? ["short"] : ["long", "short"];
312
348
  const suppressedDirection = directionPolicy === "long_only" ? "short" : directionPolicy === "short_only" ? "long" : null;
313
- const hasHistoricalEdge = (entry) => entry.pnl > 0 && entry.profitFactor >= 1 && activeDirections.every(
314
- (direction) => entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
349
+ const hasHistoricalEdge = (entry, supportConditioned = false) => (supportConditioned ? (entry.independentEvents ?? entry.trades ?? 0) < MIN_SELECTION_GRADE_TERMINAL_EVENTS || entry.pnl > 0 && entry.profitFactor >= 1 : entry.pnl > 0 && entry.profitFactor >= 1) && activeDirections.every(
350
+ (direction) => supportConditioned && (entry[direction].independentEvents ?? entry[direction].trades ?? 0) < MIN_SELECTION_GRADE_TERMINAL_EVENTS || entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
315
351
  ) && (suppressedDirection == null || entry[suppressedDirection].trades === 0 && entry[suppressedDirection].pnl === 0);
316
352
  const byDays = new Map(
317
353
  input.historicalWindows.map((entry) => [entry.days, entry])
@@ -331,7 +367,16 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
331
367
  const stableTerminalWindows = REQUIRED_STABLE_TERMINAL_WINDOWS.map(
332
368
  (days) => byDays.get(days)
333
369
  ).filter((entry) => Boolean(entry));
334
- if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry))) {
370
+ if (stableTerminalWindows.some(
371
+ (entry) => !Number.isFinite(entry.independentEvents ?? entry.trades) || activeDirections.some(
372
+ (direction) => !Number.isFinite(
373
+ entry[direction].independentEvents ?? entry[direction].trades
374
+ )
375
+ )
376
+ )) {
377
+ blockers.push("HISTORICAL_MATRIX_INCOMPLETE");
378
+ }
379
+ if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry, true))) {
335
380
  blockers.push("HISTORICAL_EDGE_FAILED");
336
381
  }
337
382
  if (blockers.length) {
@@ -343,11 +388,11 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
343
388
  maxLossValue: null,
344
389
  requiresRuntimeBinding: false,
345
390
  blockers,
346
- summary: `The final ${directionPolicy} composition does not have complete positive evidence for ALL and every active direction on the frozen 3y/4y/max-window matrix.`
391
+ summary: `The final ${directionPolicy} composition fails the frozen long-window edge or a selection-grade terminal guardrail. Underpowered and diagnostic terminal cohorts do not cause this stop.`
347
392
  };
348
393
  }
349
394
  const repairAllowed = Boolean(
350
- input.recentFailure && input.recentFailure.closedTrades >= MIN_RECENT_DIRECTION_REPAIR_TRADES && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
395
+ input.recentFailure && input.recentFailure.independentEvents >= MIN_RECENT_DIRECTION_REPAIR_EVENTS && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
351
396
  );
352
397
  if (repairAllowed) {
353
398
  return {
@@ -358,7 +403,7 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
358
403
  maxLossValue: null,
359
404
  requiresRuntimeBinding: false,
360
405
  blockers: [],
361
- summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism, then rebuild the full evidence matrix."
406
+ summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism with diagnostic independent support, then rebuild the full evidence matrix."
362
407
  };
363
408
  }
364
409
  if (!input.candidateImplemented) blockers.push("CANDIDATE_NOT_IMPLEMENTED");
@@ -5179,7 +5179,7 @@ var createPortfolioReplayConnector = (connector) => {
5179
5179
  });
5180
5180
  };
5181
5181
  const getExecutionSlippageBreakdown = ({
5182
- stage,
5182
+ stage: _stage,
5183
5183
  signal
5184
5184
  }) => (0, import_trade.calculateExecutionSlippageBreakdown)({
5185
5185
  spreadBps: (0, import_trade.extractExecutionSpreadBps)(signal),
@@ -5713,7 +5713,6 @@ var runHistoricalSignalsReplay = async ({
5713
5713
  });
5714
5714
  const cycleSymbolsByTimestamp = /* @__PURE__ */ new Map();
5715
5715
  const sharedReplayKeyPrefixes = [];
5716
- let preparedSymbols = 0;
5717
5716
  let skippedSymbols = 0;
5718
5717
  const prepareBar = new import_progress4.default(
5719
5718
  "prepare :current/:total [:bar][:percent] skipped=:skipped :etas(s) :symbol",
@@ -5832,7 +5831,6 @@ var runHistoricalSignalsReplay = async ({
5832
5831
  currentIndex: 0,
5833
5832
  strategies: strategiesForSymbol
5834
5833
  };
5835
- preparedSymbols += 1;
5836
5834
  prepareBar.tick(1, {
5837
5835
  skipped: import_chalk9.default.yellow(skippedSymbols),
5838
5836
  symbol: import_chalk9.default.gray(symbol)
@@ -5862,7 +5860,7 @@ var runHistoricalSignalsReplay = async ({
5862
5860
  }
5863
5861
  );
5864
5862
  try {
5865
- for (const [cycleIndex, timestamp] of orderedTimestamps.entries()) {
5863
+ for (const timestamp of orderedTimestamps) {
5866
5864
  const cycleStartedAt = Date.now();
5867
5865
  const cycleSymbols = cycleSymbolsByTimestamp.get(timestamp) ?? [];
5868
5866
  for (const symbolRuntime of cycleSymbols) {
@@ -8234,9 +8232,6 @@ var saveAndPrintReplayExchangeComparison = async ({
8234
8232
  toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
8235
8233
  signalTimestampOffsetMs: getReplayEntryTimestampCompareOffsetMs()
8236
8234
  });
8237
- const liveSummaryByStrategy = new Map(
8238
- liveStrategySummaries.map((summary) => [summary.strategyName, summary])
8239
- );
8240
8235
  const rowByStrategy = /* @__PURE__ */ new Map();
8241
8236
  const ensureRow = (strategyName) => {
8242
8237
  const existing = rowByStrategy.get(strategyName);
@@ -5182,7 +5182,7 @@ var createPortfolioReplayConnector = (connector) => {
5182
5182
  });
5183
5183
  };
5184
5184
  const getExecutionSlippageBreakdown = ({
5185
- stage,
5185
+ stage: _stage,
5186
5186
  signal
5187
5187
  }) => (0, import_trade.calculateExecutionSlippageBreakdown)({
5188
5188
  spreadBps: (0, import_trade.extractExecutionSpreadBps)(signal),
@@ -5716,7 +5716,6 @@ var runHistoricalSignalsReplay = async ({
5716
5716
  });
5717
5717
  const cycleSymbolsByTimestamp = /* @__PURE__ */ new Map();
5718
5718
  const sharedReplayKeyPrefixes = [];
5719
- let preparedSymbols = 0;
5720
5719
  let skippedSymbols = 0;
5721
5720
  const prepareBar = new import_progress4.default(
5722
5721
  "prepare :current/:total [:bar][:percent] skipped=:skipped :etas(s) :symbol",
@@ -5835,7 +5834,6 @@ var runHistoricalSignalsReplay = async ({
5835
5834
  currentIndex: 0,
5836
5835
  strategies: strategiesForSymbol
5837
5836
  };
5838
- preparedSymbols += 1;
5839
5837
  prepareBar.tick(1, {
5840
5838
  skipped: import_chalk9.default.yellow(skippedSymbols),
5841
5839
  symbol: import_chalk9.default.gray(symbol)
@@ -5865,7 +5863,7 @@ var runHistoricalSignalsReplay = async ({
5865
5863
  }
5866
5864
  );
5867
5865
  try {
5868
- for (const [cycleIndex, timestamp] of orderedTimestamps.entries()) {
5866
+ for (const timestamp of orderedTimestamps) {
5869
5867
  const cycleStartedAt = Date.now();
5870
5868
  const cycleSymbols = cycleSymbolsByTimestamp.get(timestamp) ?? [];
5871
5869
  for (const symbolRuntime of cycleSymbols) {
@@ -8237,9 +8235,6 @@ var saveAndPrintReplayExchangeComparison = async ({
8237
8235
  toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
8238
8236
  signalTimestampOffsetMs: getReplayEntryTimestampCompareOffsetMs()
8239
8237
  });
8240
- const liveSummaryByStrategy = new Map(
8241
- liveStrategySummaries.map((summary) => [summary.strategyName, summary])
8242
- );
8243
8238
  const rowByStrategy = /* @__PURE__ */ new Map();
8244
8239
  const ensureRow = (strategyName) => {
8245
8240
  const existing = rowByStrategy.get(strategyName);
@@ -4908,8 +4908,6 @@ var createConnector = async ({
4908
4908
  var buildReplayAiAnalyses = ({
4909
4909
  runtimeTrades,
4910
4910
  runtimeSignalEvaluations,
4911
- strategy,
4912
- symbol,
4913
4911
  toleranceMs
4914
4912
  }) => {
4915
4913
  const snapshots = [
@@ -5612,7 +5610,7 @@ var buildRuntimeParityMessage = ({
5612
5610
  runtimeEntriesCount,
5613
5611
  runtimeDuplicateEntriesCount,
5614
5612
  backtestEntriesCount,
5615
- matchedCount,
5613
+ matchedCount: _matchedCount,
5616
5614
  runtimeOnlyCount,
5617
5615
  backtestOnlyCount,
5618
5616
  matchedSummary,
@@ -5736,7 +5734,7 @@ var buildRuntimeParityNoTargetsMessage = ({
5736
5734
  window,
5737
5735
  connectorName,
5738
5736
  replayEnv,
5739
- runtimeGatesEnabled,
5737
+ runtimeGatesEnabled: _runtimeGatesEnabled,
5740
5738
  userName
5741
5739
  }) => [
5742
5740
  "\u{1F9EA} <b>TradeJS runtime parity</b>",
@@ -233,7 +233,8 @@ var STRATEGY_REVISION_RE = /^sr1:[a-f0-9]{16}$/;
233
233
  var DEPLOYMENT_COMPOSITION_ID_RE = /^dc1:[a-f0-9]{16}$/;
234
234
  var REQUIRED_LONG_WINDOWS = [1095, 1460, 1825];
235
235
  var REQUIRED_STABLE_TERMINAL_WINDOWS = [365, 180, 90];
236
- var MIN_RECENT_DIRECTION_REPAIR_TRADES = 20;
236
+ var MIN_RECENT_DIRECTION_REPAIR_EVENTS = 20;
237
+ var MIN_SELECTION_GRADE_TERMINAL_EVENTS = 50;
237
238
  var safeSegment = import_strategyReleaseEvidence.safeStrategyEvidenceSegment;
238
239
  var compactTimestamp = import_strategyReleaseEvidence.compactStrategyEvidenceTimestamp;
239
240
  var verifyFullPeriodChartArtifact = async (input) => {
@@ -252,16 +253,51 @@ var verifyFullPeriodChartArtifact = async (input) => {
252
253
  return false;
253
254
  }
254
255
  };
256
+ var readVerifiedJsonArtifact = async (artifact) => {
257
+ if (!artifact?.path || !SHA256_RE.test(artifact.sha256)) return null;
258
+ try {
259
+ const artifactPath = import_node_path.default.resolve(artifact.path);
260
+ if (await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(artifactPath) !== artifact.sha256) {
261
+ return null;
262
+ }
263
+ return JSON.parse(await import_promises.default.readFile(artifactPath, "utf8"));
264
+ } catch {
265
+ return null;
266
+ }
267
+ };
268
+ var verifyResearchDecisionLineage = async (input) => {
269
+ const progress = await readVerifiedJsonArtifact(input.progressArtifact);
270
+ const selected = await readVerifiedJsonArtifact(
271
+ input.selectedCompositionArtifact
272
+ );
273
+ if (!progress || !selected) return false;
274
+ const progressSelection = progress.selectedComposition;
275
+ return Boolean(
276
+ progress.schema === "tradejs-release-progress/v2" && progress.strategy === input.strategy && progress.status === "complete" && progress.verdictAllowed === true && typeof progress.lineageId === "string" && progress.lineageId.length > 0 && typeof progress.objectiveFingerprint === "string" && SHA256_RE.test(progress.objectiveFingerprint) && typeof progressSelection?.candidateId === "string" && progressSelection.candidateId.length > 0 && typeof progressSelection.compositionFingerprint === "string" && SHA256_RE.test(progressSelection.compositionFingerprint) && progressSelection.artifactSha256 === input.selectedCompositionArtifact?.sha256 && selected.schema === "tradejs-release-selected-composition/v2" && selected.strategy === input.strategy && selected.lineageId === progress.lineageId && selected.objectiveFingerprint === progress.objectiveFingerprint && selected.candidateId === progressSelection.candidateId && selected.compositionFingerprint === progressSelection.compositionFingerprint && selected.chartSha256 === input.chartArtifact?.sha256
277
+ );
278
+ };
255
279
  var hasExactRuntimeTarget = (target, strategyName) => Boolean(
256
280
  target?.userName.trim() && target.deploymentId.trim() && target.accountId.trim() && target.strategyName.trim() === strategyName.trim() && STRATEGY_REVISION_RE.test(target.strategyRevision) && DEPLOYMENT_COMPOSITION_ID_RE.test(target.deploymentCompositionId)
257
281
  );
258
282
  var deriveStrategyReleaseResearchDecision = async (input) => {
259
283
  const blockers = [];
284
+ if (!await verifyResearchDecisionLineage(input)) {
285
+ return {
286
+ strategy: input.strategy,
287
+ action: "FORWARD_BLOCKED",
288
+ repairAllowed: false,
289
+ targetDirection: input.recentFailure?.direction ?? null,
290
+ maxLossValue: null,
291
+ requiresRuntimeBinding: false,
292
+ blockers: ["RESEARCH_LINEAGE_MISMATCH"],
293
+ summary: "The progress decision, selected composition, objective, and chart do not resolve to one immutable research lineage."
294
+ };
295
+ }
260
296
  const directionPolicy = input.directionPolicy ?? "both";
261
297
  const activeDirections = directionPolicy === "long_only" ? ["long"] : directionPolicy === "short_only" ? ["short"] : ["long", "short"];
262
298
  const suppressedDirection = directionPolicy === "long_only" ? "short" : directionPolicy === "short_only" ? "long" : null;
263
- const hasHistoricalEdge = (entry) => entry.pnl > 0 && entry.profitFactor >= 1 && activeDirections.every(
264
- (direction) => entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
299
+ const hasHistoricalEdge = (entry, supportConditioned = false) => (supportConditioned ? (entry.independentEvents ?? entry.trades ?? 0) < MIN_SELECTION_GRADE_TERMINAL_EVENTS || entry.pnl > 0 && entry.profitFactor >= 1 : entry.pnl > 0 && entry.profitFactor >= 1) && activeDirections.every(
300
+ (direction) => supportConditioned && (entry[direction].independentEvents ?? entry[direction].trades ?? 0) < MIN_SELECTION_GRADE_TERMINAL_EVENTS || entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
265
301
  ) && (suppressedDirection == null || entry[suppressedDirection].trades === 0 && entry[suppressedDirection].pnl === 0);
266
302
  const byDays = new Map(
267
303
  input.historicalWindows.map((entry) => [entry.days, entry])
@@ -281,7 +317,16 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
281
317
  const stableTerminalWindows = REQUIRED_STABLE_TERMINAL_WINDOWS.map(
282
318
  (days) => byDays.get(days)
283
319
  ).filter((entry) => Boolean(entry));
284
- if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry))) {
320
+ if (stableTerminalWindows.some(
321
+ (entry) => !Number.isFinite(entry.independentEvents ?? entry.trades) || activeDirections.some(
322
+ (direction) => !Number.isFinite(
323
+ entry[direction].independentEvents ?? entry[direction].trades
324
+ )
325
+ )
326
+ )) {
327
+ blockers.push("HISTORICAL_MATRIX_INCOMPLETE");
328
+ }
329
+ if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry, true))) {
285
330
  blockers.push("HISTORICAL_EDGE_FAILED");
286
331
  }
287
332
  if (blockers.length) {
@@ -293,11 +338,11 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
293
338
  maxLossValue: null,
294
339
  requiresRuntimeBinding: false,
295
340
  blockers,
296
- summary: `The final ${directionPolicy} composition does not have complete positive evidence for ALL and every active direction on the frozen 3y/4y/max-window matrix.`
341
+ summary: `The final ${directionPolicy} composition fails the frozen long-window edge or a selection-grade terminal guardrail. Underpowered and diagnostic terminal cohorts do not cause this stop.`
297
342
  };
298
343
  }
299
344
  const repairAllowed = Boolean(
300
- input.recentFailure && input.recentFailure.closedTrades >= MIN_RECENT_DIRECTION_REPAIR_TRADES && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
345
+ input.recentFailure && input.recentFailure.independentEvents >= MIN_RECENT_DIRECTION_REPAIR_EVENTS && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
301
346
  );
302
347
  if (repairAllowed) {
303
348
  return {
@@ -308,7 +353,7 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
308
353
  maxLossValue: null,
309
354
  requiresRuntimeBinding: false,
310
355
  blockers: [],
311
- summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism, then rebuild the full evidence matrix."
356
+ summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism with diagnostic independent support, then rebuild the full evidence matrix."
312
357
  };
313
358
  }
314
359
  if (!input.candidateImplemented) blockers.push("CANDIDATE_NOT_IMPLEMENTED");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "3.1.14",
3
+ "version": "3.1.15-beta.228",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -36,13 +36,13 @@
36
36
  "tradejs": "dist/cli.js"
37
37
  },
38
38
  "dependencies": {
39
- "@tradejs/connectors": "^3.1.14",
40
- "@tradejs/core": "^3.1.14",
41
- "@tradejs/indicators": "^3.1.14",
42
- "@tradejs/infra": "^3.1.14",
43
- "@tradejs/node": "^3.1.14",
39
+ "@tradejs/connectors": "^3.1.15-beta.228",
40
+ "@tradejs/core": "^3.1.15-beta.228",
41
+ "@tradejs/indicators": "^3.1.15-beta.228",
42
+ "@tradejs/infra": "^3.1.15-beta.228",
43
+ "@tradejs/node": "^3.1.15-beta.228",
44
44
  "@tradejs/strategy-kit": "3.0.3",
45
- "@tradejs/types": "^3.1.14",
45
+ "@tradejs/types": "^3.1.15-beta.228",
46
46
  "args": "^5.0.3",
47
47
  "bcryptjs": "^2.4.3",
48
48
  "chalk": "4.1.2",