@tradejs/cli 2.0.18 → 2.0.20

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 (56) hide show
  1. package/dist/cli.js +14522 -15704
  2. package/dist/lib/aiExport.js +15 -1608
  3. package/dist/lib/aiPocketSearch.js +499 -67
  4. package/dist/lib/aiPocketSearchCli.js +113 -0
  5. package/dist/lib/aiTrainCharts.js +14 -1
  6. package/dist/lib/aiTrainMetrics.js +4 -1
  7. package/dist/lib/aiTrainResearch.js +88 -54
  8. package/dist/lib/binanceMarketContextBackfill.js +7 -6
  9. package/dist/lib/coinMarketCapContextBackfill.js +19 -18
  10. package/dist/lib/coreResearch.js +2738 -0
  11. package/dist/lib/derivativesContextBackfill.js +10 -9
  12. package/dist/lib/executionCalibration.js +6 -3
  13. package/dist/lib/hyperliquidWhaleBackfill.js +8 -8
  14. package/dist/lib/hyperliquidWhaleStream.js +4 -4
  15. package/dist/lib/marketContextPrepare.js +83 -1686
  16. package/dist/lib/runEnvironment.js +14 -1666
  17. package/dist/lib/runFormatting.js +1 -1607
  18. package/dist/lib/runtimeDebugEvidence.js +2 -48
  19. package/dist/lib/runtimeLineage.js +81 -48
  20. package/dist/lib/runtimeRedis.js +6 -57
  21. package/dist/lib/runtimeScorecard.js +153 -10
  22. package/dist/lib/runtimeStrategyBacktest.js +2 -48
  23. package/dist/lib/strategyGateFingerprint.js +80 -0
  24. package/dist/lib/strategyRelease.js +1365 -0
  25. package/dist/scripts/aiExport.js +17 -1610
  26. package/dist/scripts/aiPocketSearch.js +1070 -1770
  27. package/dist/scripts/aiTrain.js +108 -1688
  28. package/dist/scripts/backtest.js +120 -1760
  29. package/dist/scripts/binanceMarketIngest.js +5 -4
  30. package/dist/scripts/candlesMigrateProvider.js +6 -5186
  31. package/dist/scripts/cleanupMarketContext.js +4 -3
  32. package/dist/scripts/continuity.js +4 -3
  33. package/dist/scripts/coreResearch.js +2830 -0
  34. package/dist/scripts/derivativesIngest.js +6 -4
  35. package/dist/scripts/derivativesIngestCoinalyzeAll.js +5 -4
  36. package/dist/scripts/doctor.js +2 -5184
  37. package/dist/scripts/executionCalibration.js +6 -3
  38. package/dist/scripts/hyperliquidWhaleBackfill.js +8 -8
  39. package/dist/scripts/hyperliquidWhaleIngest.js +4 -4
  40. package/dist/scripts/hyperliquidWhalesUpdate.js +1 -1
  41. package/dist/scripts/migration.js +3 -3
  42. package/dist/scripts/mlExport.js +1 -1607
  43. package/dist/scripts/replay.js +239 -1855
  44. package/dist/scripts/replayRunner.js +239 -1855
  45. package/dist/scripts/replayRuntimeEvidence.js +29 -62
  46. package/dist/scripts/researchAuto.js +3 -49
  47. package/dist/scripts/results.js +1 -1607
  48. package/dist/scripts/runtimeEvidence.js +18 -56
  49. package/dist/scripts/runtimeParity.js +121 -1746
  50. package/dist/scripts/runtimeScorecard.js +883 -29
  51. package/dist/scripts/signals.js +1178 -2724
  52. package/dist/scripts/signalsDaemon.js +1183 -2696
  53. package/dist/scripts/signalsSummary.js +4 -26
  54. package/dist/scripts/strategyRelease.js +1469 -0
  55. package/dist/workers/testerWorker.js +2 -2
  56. package/package.json +13 -9
@@ -0,0 +1,1365 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
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
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/lib/strategyRelease.ts
31
+ var strategyRelease_exports = {};
32
+ __export(strategyRelease_exports, {
33
+ buildStrategyLiveDiagnosis: () => buildStrategyLiveDiagnosis,
34
+ buildStrategyLiveDiagnosisFromScorecard: () => buildStrategyLiveDiagnosisFromScorecard,
35
+ buildStrategyMonitoringProfile: () => buildStrategyMonitoringProfile,
36
+ canonicalStrategyReleaseJson: () => canonicalStrategyReleaseJson,
37
+ collectReleaseEvidenceReferences: () => collectReleaseEvidenceReferences,
38
+ createStrategyEvidenceMarkerEnvelope: () => import_strategyReleaseEvidence.createStrategyEvidenceMarkerEnvelope,
39
+ createStrategyReleaseEnvelope: () => createStrategyReleaseEnvelope,
40
+ createStrategyReleaseManifest: () => createStrategyReleaseManifest,
41
+ deriveStrategyReleaseResearchDecision: () => deriveStrategyReleaseResearchDecision,
42
+ planStrategyEvidenceRetention: () => planStrategyEvidenceRetention,
43
+ publishStrategyLiveDiagnosis: () => publishStrategyLiveDiagnosis,
44
+ publishStrategyRelease: () => publishStrategyRelease,
45
+ strategyReleaseSha256: () => strategyReleaseSha256,
46
+ verifyStrategyEvidenceMarkerEnvelope: () => verifyStrategyEvidenceMarkerEnvelope,
47
+ verifyStrategyReleaseEnvelope: () => verifyStrategyReleaseEnvelope
48
+ });
49
+ module.exports = __toCommonJS(strategyRelease_exports);
50
+ var import_node_crypto = require("crypto");
51
+ var import_promises = __toESM(require("fs/promises"));
52
+ var import_node_path = __toESM(require("path"));
53
+ var import_types = require("@tradejs/types");
54
+ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
55
+ var SHA256_RE = /^[a-f0-9]{64}$/;
56
+ var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
57
+ var DAY_MS = 864e5;
58
+ var REQUIRED_LONG_WINDOWS = [1095, 1460, 1825];
59
+ var REQUIRED_STABLE_TERMINAL_WINDOWS = [365, 180, 90];
60
+ var MIN_RECENT_DIRECTION_REPAIR_TRADES = 20;
61
+ var safeSegment = import_strategyReleaseEvidence.safeStrategyEvidenceSegment;
62
+ var compactTimestamp = import_strategyReleaseEvidence.compactStrategyEvidenceTimestamp;
63
+ var verifyFullPeriodChartArtifact = async (input) => {
64
+ const artifact = input.chartArtifact;
65
+ if (!artifact?.path || !SHA256_RE.test(artifact.sha256)) return false;
66
+ try {
67
+ const artifactPath = import_node_path.default.resolve(artifact.path);
68
+ if (await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(artifactPath) !== artifact.sha256) {
69
+ return false;
70
+ }
71
+ const report = JSON.parse(await import_promises.default.readFile(artifactPath, "utf8"));
72
+ return Boolean(
73
+ report.chart?.persisted === true && report.chart.cardIds?.length && report.errors?.failed === 0 && report.run?.strategy === input.strategy && report.run.mode === "local-deterministic" && report.run.recent === 0 && report.run.since == null && report.run.until == null && Number(report.run.sourceRows) > 0
74
+ );
75
+ } catch {
76
+ return false;
77
+ }
78
+ };
79
+ var hasExactRuntimeTarget = (target) => Boolean(
80
+ target?.userName.trim() && target.deploymentId.trim() && target.accountId.trim() && target.strategyConfigName.trim()
81
+ );
82
+ var deriveStrategyReleaseResearchDecision = async (input) => {
83
+ const blockers = [];
84
+ const byDays = new Map(
85
+ input.historicalWindows.map((entry) => [entry.days, entry])
86
+ );
87
+ if (REQUIRED_LONG_WINDOWS.some((days) => !byDays.has(days))) {
88
+ blockers.push("HISTORICAL_MATRIX_INCOMPLETE");
89
+ }
90
+ if (REQUIRED_STABLE_TERMINAL_WINDOWS.some((days) => !byDays.has(days))) {
91
+ blockers.push("HISTORICAL_MATRIX_INCOMPLETE");
92
+ }
93
+ const longWindows = REQUIRED_LONG_WINDOWS.map(
94
+ (days) => byDays.get(days)
95
+ ).filter((entry) => Boolean(entry));
96
+ if (longWindows.some(
97
+ (entry) => entry.pnl <= 0 || entry.profitFactor < 1 || entry.long.pnl <= 0 || entry.long.profitFactor < 1 || entry.short.pnl <= 0 || entry.short.profitFactor < 1
98
+ )) {
99
+ blockers.push("HISTORICAL_EDGE_FAILED");
100
+ }
101
+ const stableTerminalWindows = REQUIRED_STABLE_TERMINAL_WINDOWS.map(
102
+ (days) => byDays.get(days)
103
+ ).filter((entry) => Boolean(entry));
104
+ if (stableTerminalWindows.some(
105
+ (entry) => entry.pnl <= 0 || entry.profitFactor < 1 || entry.long.pnl <= 0 || entry.long.profitFactor < 1 || entry.short.pnl <= 0 || entry.short.profitFactor < 1
106
+ )) {
107
+ blockers.push("HISTORICAL_EDGE_FAILED");
108
+ }
109
+ if (blockers.length) {
110
+ return {
111
+ strategy: input.strategy,
112
+ action: "STOP_RESEARCH",
113
+ repairAllowed: false,
114
+ targetDirection: input.recentFailure?.direction ?? null,
115
+ maxLossValue: null,
116
+ blockers,
117
+ summary: "The final composition does not have complete positive ALL/LONG/SHORT evidence on the frozen 3y/4y/max-window matrix."
118
+ };
119
+ }
120
+ const repairAllowed = Boolean(
121
+ input.recentFailure && input.recentFailure.closedTrades >= MIN_RECENT_DIRECTION_REPAIR_TRADES && input.recentFailure.causalMechanismIdentified && input.recentFailure.repairRoundsUsed === 0 && !input.exposedEvaluation
122
+ );
123
+ if (repairAllowed) {
124
+ return {
125
+ strategy: input.strategy,
126
+ action: "REPAIR_RECENT_DIRECTION",
127
+ repairAllowed: true,
128
+ targetDirection: input.recentFailure.direction,
129
+ maxLossValue: null,
130
+ blockers: [],
131
+ summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism, then rebuild the full evidence matrix."
132
+ };
133
+ }
134
+ if (!input.candidateImplemented) blockers.push("CANDIDATE_NOT_IMPLEMENTED");
135
+ if (!await verifyFullPeriodChartArtifact(input)) {
136
+ blockers.push("FULL_PERIOD_CHART_MISSING");
137
+ }
138
+ if (input.forwardTest.maxLossValue !== 1) {
139
+ blockers.push("FORWARD_RISK_MUST_BE_ONE");
140
+ }
141
+ if (blockers.length) {
142
+ return {
143
+ strategy: input.strategy,
144
+ action: "FORWARD_BLOCKED",
145
+ repairAllowed: false,
146
+ targetDirection: input.recentFailure?.direction ?? null,
147
+ maxLossValue: null,
148
+ blockers,
149
+ summary: "Complete the frozen candidate implementation and full-period chart before any micro-forward execution."
150
+ };
151
+ }
152
+ if (!input.forwardTest.authorized) {
153
+ return {
154
+ strategy: input.strategy,
155
+ action: "MICRO_FORWARD_READY",
156
+ repairAllowed: false,
157
+ targetDirection: input.recentFailure?.direction ?? null,
158
+ maxLossValue: 1,
159
+ blockers: ["FORWARD_NOT_AUTHORIZED"],
160
+ summary: "The candidate is ready for a separately authorized micro-forward test at MAX_LOSS_VALUE=1."
161
+ };
162
+ }
163
+ if (!hasExactRuntimeTarget(input.forwardTest.runtimeTarget)) {
164
+ return {
165
+ strategy: input.strategy,
166
+ action: "FORWARD_BLOCKED",
167
+ repairAllowed: false,
168
+ targetDirection: input.recentFailure?.direction ?? null,
169
+ maxLossValue: 1,
170
+ blockers: ["RUNTIME_TARGET_UNRESOLVED"],
171
+ summary: "Forward testing is authorized, but an exact runtime deployment/account target must be resolved first."
172
+ };
173
+ }
174
+ return {
175
+ strategy: input.strategy,
176
+ action: "START_MICRO_FORWARD",
177
+ repairAllowed: false,
178
+ targetDirection: input.recentFailure?.direction ?? null,
179
+ maxLossValue: 1,
180
+ blockers: [],
181
+ summary: "Start the exact frozen composition as a micro-forward test at MAX_LOSS_VALUE=1; do not retune an underpowered or exposed recent tail."
182
+ };
183
+ };
184
+ var canonicalStrategyReleaseJson = (value) => (0, import_strategyReleaseEvidence.canonicalStrategyEvidenceJson)(value);
185
+ var strategyReleaseSha256 = (value) => (0, import_strategyReleaseEvidence.strategyEvidenceSha256)(value);
186
+ function assert(condition, message) {
187
+ if (!condition) throw new Error(`Invalid strategy release: ${message}`);
188
+ }
189
+ var assertFingerprint = (value, label) => assert(SHA256_RE.test(value), `${label} must be a lowercase SHA-256`);
190
+ var assertLineageFingerprint = (value, label) => assert(
191
+ LINEAGE_FINGERPRINT_RE.test(value),
192
+ `${label} must be a 16-character lowercase lineage fingerprint`
193
+ );
194
+ var releaseReasons = (input) => {
195
+ const reasons = [];
196
+ const requiredEvidenceKinds = [
197
+ "core_research",
198
+ "ai_gate",
199
+ "runtime_parity",
200
+ "execution_calibration"
201
+ ];
202
+ if (input.evidence.some((entry) => !entry.verified) || requiredEvidenceKinds.some(
203
+ (kind) => !input.evidence.some((entry) => entry.kind === kind)
204
+ )) {
205
+ reasons.push("EVIDENCE_INCOMPLETE");
206
+ }
207
+ if (!input.gates.coreEdgeVerified) reasons.push("NO_VERIFIED_CORE_EDGE");
208
+ if (!input.gates.aiGateAddsValue) reasons.push("AI_GATE_ADDS_NO_VALUE");
209
+ if (!input.gates.currentMarketSuitable) {
210
+ reasons.push("CURRENT_REGIME_UNSUITABLE");
211
+ }
212
+ if (!input.gates.runtimeParityVerified) {
213
+ reasons.push("RUNTIME_PARITY_BLOCKED");
214
+ }
215
+ if (!input.gates.executionModelVerified) {
216
+ reasons.push("EXECUTION_MODEL_UNSAFE");
217
+ }
218
+ if (input.researchBudget.hypothesisFamilies >= 3 && !input.gates.coreEdgeVerified) {
219
+ reasons.push("RESEARCH_BUDGET_EXHAUSTED");
220
+ }
221
+ return [...new Set(reasons)];
222
+ };
223
+ var deriveReleaseGates = (evidence) => {
224
+ const asserted = (name) => {
225
+ const values = evidence.filter((entry) => entry.verified).map((entry) => entry.releaseAssertions?.[name]).filter((value) => typeof value === "boolean");
226
+ return values.length > 0 && values.every(Boolean);
227
+ };
228
+ return {
229
+ coreEdgeVerified: asserted("coreEdgeVerified"),
230
+ aiGateAddsValue: asserted("aiGateAddsValue"),
231
+ currentMarketSuitable: asserted("currentMarketSuitable"),
232
+ runtimeParityVerified: asserted("runtimeParityVerified"),
233
+ executionModelVerified: asserted("executionModelVerified")
234
+ };
235
+ };
236
+ var verifyEvidenceComposition = (strategy, composition, evidence) => {
237
+ const requireEqual = (actual, expected, label, artifactId) => assert(
238
+ actual === expected,
239
+ `evidence ${artifactId} ${label} does not match the frozen composition`
240
+ );
241
+ for (const reference of evidence) {
242
+ const lineage = reference.lineage;
243
+ if (reference.kind === "core_research") {
244
+ requireEqual(
245
+ lineage?.strategy,
246
+ strategy,
247
+ "strategy",
248
+ reference.artifactId
249
+ );
250
+ requireEqual(
251
+ lineage?.coreConfigSha256,
252
+ composition.coreConfigSha256,
253
+ "coreConfigSha256",
254
+ reference.artifactId
255
+ );
256
+ requireEqual(
257
+ lineage?.gitSha,
258
+ composition.gitSha,
259
+ "gitSha",
260
+ reference.artifactId
261
+ );
262
+ requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
263
+ requireEqual(
264
+ lineage?.maxLossValue,
265
+ composition.maxLossValue,
266
+ "MAX_LOSS_VALUE",
267
+ reference.artifactId
268
+ );
269
+ assert(
270
+ lineage?.sourceSha256s.includes(composition.coreExportSha256),
271
+ `evidence ${reference.artifactId} does not include the frozen core export`
272
+ );
273
+ continue;
274
+ }
275
+ if (reference.kind === "ai_gate") {
276
+ requireEqual(
277
+ lineage?.strategy,
278
+ strategy,
279
+ "strategy",
280
+ reference.artifactId
281
+ );
282
+ requireEqual(
283
+ lineage?.gitSha,
284
+ composition.gitSha,
285
+ "gitSha",
286
+ reference.artifactId
287
+ );
288
+ requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
289
+ requireEqual(
290
+ lineage?.gateConfigIdsFingerprint,
291
+ composition.gateConfigIdsFingerprint,
292
+ "gateConfigIdsFingerprint",
293
+ reference.artifactId
294
+ );
295
+ requireEqual(
296
+ lineage?.gateFingerprint,
297
+ composition.gateFingerprint,
298
+ "gateFingerprint",
299
+ reference.artifactId
300
+ );
301
+ requireEqual(
302
+ lineage?.gateContextFingerprint,
303
+ composition.gateContextFingerprint,
304
+ "gateContextFingerprint",
305
+ reference.artifactId
306
+ );
307
+ assert(
308
+ lineage?.sourceSha256s.includes(composition.coreExportSha256),
309
+ `evidence ${reference.artifactId} does not use the frozen core export`
310
+ );
311
+ continue;
312
+ }
313
+ if (reference.kind === "runtime_parity") {
314
+ requireEqual(
315
+ lineage?.strategy,
316
+ strategy,
317
+ "strategy",
318
+ reference.artifactId
319
+ );
320
+ requireEqual(
321
+ lineage?.gitSha,
322
+ composition.gitSha,
323
+ "gitSha",
324
+ reference.artifactId
325
+ );
326
+ requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
327
+ requireEqual(
328
+ lineage?.gateFingerprint,
329
+ composition.gateFingerprint,
330
+ "gateFingerprint",
331
+ reference.artifactId
332
+ );
333
+ requireEqual(
334
+ lineage?.runtimeConfigFingerprint,
335
+ composition.runtimeConfigFingerprint,
336
+ "runtimeConfigFingerprint",
337
+ reference.artifactId
338
+ );
339
+ requireEqual(
340
+ lineage?.runtimeContextFingerprint,
341
+ composition.runtimeContextFingerprint,
342
+ "runtimeContextFingerprint",
343
+ reference.artifactId
344
+ );
345
+ requireEqual(
346
+ lineage?.maxLossValue,
347
+ composition.maxLossValue,
348
+ "MAX_LOSS_VALUE",
349
+ reference.artifactId
350
+ );
351
+ continue;
352
+ }
353
+ if (reference.kind === "execution_calibration") {
354
+ requireEqual(
355
+ lineage?.strategy,
356
+ strategy,
357
+ "strategy",
358
+ reference.artifactId
359
+ );
360
+ requireEqual(
361
+ lineage?.gitSha,
362
+ composition.gitSha,
363
+ "gitSha",
364
+ reference.artifactId
365
+ );
366
+ requireEqual(lineage?.gitDirty, false, "gitDirty", reference.artifactId);
367
+ requireEqual(
368
+ lineage?.gateFingerprint,
369
+ composition.gateFingerprint,
370
+ "gateFingerprint",
371
+ reference.artifactId
372
+ );
373
+ requireEqual(
374
+ lineage?.runtimeConfigFingerprint,
375
+ composition.runtimeConfigFingerprint,
376
+ "runtimeConfigFingerprint",
377
+ reference.artifactId
378
+ );
379
+ requireEqual(
380
+ lineage?.runtimeContextFingerprint,
381
+ composition.runtimeContextFingerprint,
382
+ "runtimeContextFingerprint",
383
+ reference.artifactId
384
+ );
385
+ requireEqual(
386
+ lineage?.maxLossValue,
387
+ composition.maxLossValue,
388
+ "MAX_LOSS_VALUE",
389
+ reference.artifactId
390
+ );
391
+ }
392
+ }
393
+ };
394
+ var createStrategyReleaseManifest = (input) => {
395
+ assert(input.strategy.trim(), "strategy is required");
396
+ assert(
397
+ Number.isFinite(input.createdAt),
398
+ "createdAt must be a finite timestamp"
399
+ );
400
+ assert(
401
+ input.researchBudget.hypothesisFamilies <= 3,
402
+ "at most 3 hypothesis families are allowed"
403
+ );
404
+ assert(
405
+ input.researchBudget.maximumVariantsPerFamily <= 5,
406
+ "at most 5 variants per family are allowed"
407
+ );
408
+ assert(
409
+ input.researchBudget.isolatedLongFinalists <= 1,
410
+ "at most one isolated-long finalist is allowed"
411
+ );
412
+ assert(
413
+ input.researchBudget.aiGateTuningRounds <= 1,
414
+ "at most one AI-gate tuning round is allowed"
415
+ );
416
+ assert(input.marketWindow.cacheOnly, "market window must be cache-only");
417
+ assert(
418
+ input.marketWindow.endTime > input.marketWindow.startTime,
419
+ "market window is invalid"
420
+ );
421
+ assert(input.composition.maxLossValue > 0, "MAX_LOSS_VALUE must be positive");
422
+ assert(
423
+ input.composition.longEnabled && input.composition.shortEnabled,
424
+ "LONG and SHORT must both remain enabled"
425
+ );
426
+ assert(
427
+ Number.isInteger(input.monitoring.minimumProspectiveClosedTrades) && input.monitoring.minimumProspectiveClosedTrades > 0,
428
+ "monitoring.minimumProspectiveClosedTrades must be positive"
429
+ );
430
+ assert(
431
+ Number.isFinite(input.monitoring.minimumParityRatio) && input.monitoring.minimumParityRatio >= 0 && input.monitoring.minimumParityRatio <= 1,
432
+ "monitoring.minimumParityRatio must be between 0 and 1"
433
+ );
434
+ assert(
435
+ Number.isFinite(input.monitoring.maximumOrderFailureRate) && input.monitoring.maximumOrderFailureRate >= 0 && input.monitoring.maximumOrderFailureRate <= 1,
436
+ "monitoring.maximumOrderFailureRate must be between 0 and 1"
437
+ );
438
+ assert(
439
+ Number.isFinite(input.monitoring.minimumRegimeCoverage) && input.monitoring.minimumRegimeCoverage >= 0 && input.monitoring.minimumRegimeCoverage <= 1,
440
+ "monitoring.minimumRegimeCoverage must be between 0 and 1"
441
+ );
442
+ assert(
443
+ input.monitoring.drawdownEnvelopes.length > 0 && input.monitoring.drawdownEnvelopes.every(
444
+ (entry) => Number.isInteger(entry.days) && entry.days > 0 && Number.isFinite(entry.p95) && Number.isFinite(entry.maximum) && entry.p95 >= 0 && entry.maximum >= entry.p95
445
+ ),
446
+ "monitoring drawdown envelopes are invalid"
447
+ );
448
+ assertFingerprint(input.composition.coreConfigSha256, "coreConfigSha256");
449
+ assertFingerprint(input.composition.coreExportSha256, "coreExportSha256");
450
+ assertLineageFingerprint(
451
+ input.composition.gateConfigIdsFingerprint,
452
+ "gateConfigIdsFingerprint"
453
+ );
454
+ assertLineageFingerprint(
455
+ input.composition.runtimeConfigFingerprint,
456
+ "runtimeConfigFingerprint"
457
+ );
458
+ assertLineageFingerprint(
459
+ input.composition.gateFingerprint,
460
+ "gateFingerprint"
461
+ );
462
+ assertLineageFingerprint(
463
+ input.composition.gateContextFingerprint,
464
+ "gateContextFingerprint"
465
+ );
466
+ assertLineageFingerprint(
467
+ input.composition.runtimeContextFingerprint,
468
+ "runtimeContextFingerprint"
469
+ );
470
+ assertFingerprint(input.marketWindow.universeSha256, "universeSha256");
471
+ for (const entry of input.evidence) {
472
+ assertFingerprint(entry.sha256, `evidence ${entry.artifactId}`);
473
+ }
474
+ verifyEvidenceComposition(input.strategy, input.composition, input.evidence);
475
+ const evidenceGates = deriveReleaseGates(input.evidence);
476
+ assert(
477
+ canonicalStrategyReleaseJson(input.gates) === canonicalStrategyReleaseJson(evidenceGates),
478
+ "release gates must be derived from verified evidence assertions"
479
+ );
480
+ const compositionIdentity = {
481
+ strategy: input.strategy,
482
+ gitSha: input.composition.gitSha,
483
+ coreConfigSha256: input.composition.coreConfigSha256,
484
+ coreExportSha256: input.composition.coreExportSha256,
485
+ gateConfigIdsFingerprint: input.composition.gateConfigIdsFingerprint,
486
+ runtimeConfigFingerprint: input.composition.runtimeConfigFingerprint,
487
+ gateFingerprint: input.composition.gateFingerprint,
488
+ gateContextFingerprint: input.composition.gateContextFingerprint,
489
+ runtimeContextFingerprint: input.composition.runtimeContextFingerprint,
490
+ maxLossValue: input.composition.maxLossValue
491
+ };
492
+ const compositionId = `${safeSegment(input.strategy, "strategy")}_${strategyReleaseSha256(compositionIdentity).slice(0, 16)}`;
493
+ const reasons = releaseReasons({ ...input, gates: evidenceGates });
494
+ const economicFailure = reasons.some(
495
+ (reason) => [
496
+ "NO_VERIFIED_CORE_EDGE",
497
+ "AI_GATE_ADDS_NO_VALUE",
498
+ "CURRENT_REGIME_UNSUITABLE",
499
+ "RESEARCH_BUDGET_EXHAUSTED"
500
+ ].includes(reason)
501
+ );
502
+ const evidenceFailure = reasons.some(
503
+ (reason) => [
504
+ "EVIDENCE_INCOMPLETE",
505
+ "RUNTIME_PARITY_BLOCKED",
506
+ "EXECUTION_MODEL_UNSAFE"
507
+ ].includes(reason)
508
+ );
509
+ const verdict = evidenceFailure ? "INSUFFICIENT_EVIDENCE" : economicFailure ? "UNSUITABLE_FOR_CURRENT_MARKET" : "READY_FOR_RUNTIME";
510
+ const releaseIdentity = {
511
+ strategy: input.strategy,
512
+ createdAt: input.createdAt,
513
+ compositionId,
514
+ evidence: input.evidence.map(({ kind, artifactId, sha256 }) => ({
515
+ kind,
516
+ artifactId,
517
+ sha256
518
+ })),
519
+ verdict
520
+ };
521
+ return {
522
+ schema: import_types.STRATEGY_RELEASE_SCHEMA,
523
+ releaseId: `${safeSegment(input.strategy, "strategy")}_${compactTimestamp(input.createdAt)}_${strategyReleaseSha256(releaseIdentity).slice(0, 16)}`,
524
+ strategy: input.strategy,
525
+ createdAt: input.createdAt,
526
+ composition: { ...input.composition, compositionId },
527
+ marketWindow: input.marketWindow,
528
+ researchBudget: input.researchBudget,
529
+ evidence: input.evidence,
530
+ gates: evidenceGates,
531
+ monitoring: input.monitoring,
532
+ verdict: {
533
+ status: verdict,
534
+ reasons,
535
+ summary: input.summary
536
+ },
537
+ prospective: input.prospective
538
+ };
539
+ };
540
+ var createStrategyReleaseEnvelope = (manifest) => ({
541
+ schema: "tradejs-strategy-release-envelope/v1",
542
+ releaseId: manifest.releaseId,
543
+ manifestSha256: strategyReleaseSha256(manifest),
544
+ manifest
545
+ });
546
+ var verifyStrategyReleaseEnvelope = async (valueOrPath) => {
547
+ const value = typeof valueOrPath === "string" ? JSON.parse(await import_promises.default.readFile(valueOrPath, "utf8")) : valueOrPath;
548
+ const envelope = value;
549
+ assert(
550
+ envelope?.schema === "tradejs-strategy-release-envelope/v1",
551
+ "release envelope schema mismatch"
552
+ );
553
+ assert(
554
+ envelope.releaseId === envelope.manifest?.releaseId,
555
+ "releaseId mismatch"
556
+ );
557
+ assert(
558
+ strategyReleaseSha256(envelope.manifest) === envelope.manifestSha256,
559
+ "release manifest checksum mismatch"
560
+ );
561
+ const manifest = envelope.manifest;
562
+ assert(
563
+ manifest.schema === import_types.STRATEGY_RELEASE_SCHEMA,
564
+ "manifest schema mismatch"
565
+ );
566
+ const rebuilt = createStrategyReleaseManifest({
567
+ strategy: manifest.strategy,
568
+ createdAt: manifest.createdAt,
569
+ composition: {
570
+ gitSha: manifest.composition.gitSha,
571
+ coreConfigSha256: manifest.composition.coreConfigSha256,
572
+ coreExportSha256: manifest.composition.coreExportSha256,
573
+ gateConfigIdsFingerprint: manifest.composition.gateConfigIdsFingerprint,
574
+ runtimeConfigFingerprint: manifest.composition.runtimeConfigFingerprint,
575
+ gateFingerprint: manifest.composition.gateFingerprint,
576
+ gateContextFingerprint: manifest.composition.gateContextFingerprint,
577
+ runtimeContextFingerprint: manifest.composition.runtimeContextFingerprint,
578
+ maxLossValue: manifest.composition.maxLossValue,
579
+ longEnabled: manifest.composition.longEnabled,
580
+ shortEnabled: manifest.composition.shortEnabled
581
+ },
582
+ marketWindow: manifest.marketWindow,
583
+ researchBudget: manifest.researchBudget,
584
+ evidence: manifest.evidence,
585
+ gates: manifest.gates,
586
+ monitoring: manifest.monitoring,
587
+ summary: manifest.verdict.summary,
588
+ prospective: manifest.prospective
589
+ });
590
+ assert(
591
+ canonicalStrategyReleaseJson(rebuilt) === canonicalStrategyReleaseJson(manifest),
592
+ "release manifest contains self-declared or inconsistent derived fields"
593
+ );
594
+ return envelope;
595
+ };
596
+ var verifyStrategyEvidenceMarkerEnvelope = async (valueOrPath) => {
597
+ const value = typeof valueOrPath === "string" ? JSON.parse(await import_promises.default.readFile(valueOrPath, "utf8")) : valueOrPath;
598
+ return (0, import_strategyReleaseEvidence.verifyStrategyEvidenceMarkerEnvelope)(value);
599
+ };
600
+ var releaseMarkers = (manifest) => {
601
+ const common = {
602
+ timestamp: manifest.createdAt,
603
+ artifactId: manifest.releaseId,
604
+ artifactSha256: strategyReleaseSha256(manifest),
605
+ compositionId: manifest.composition.compositionId,
606
+ gitSha: manifest.composition.gitSha,
607
+ gateFingerprint: manifest.composition.gateFingerprint,
608
+ configFingerprint: manifest.composition.runtimeConfigFingerprint,
609
+ contextFingerprint: manifest.composition.runtimeContextFingerprint,
610
+ maxLossValue: manifest.composition.maxLossValue
611
+ };
612
+ return [
613
+ {
614
+ ...common,
615
+ id: `${manifest.releaseId}:evidence`,
616
+ type: "E",
617
+ label: "Evidence frozen",
618
+ summary: `${manifest.evidence.length} verified release artifacts`,
619
+ coverage: {
620
+ startTime: manifest.marketWindow.startTime,
621
+ endTime: manifest.marketWindow.endTime
622
+ }
623
+ },
624
+ {
625
+ ...common,
626
+ id: `${manifest.releaseId}:gate`,
627
+ type: "G",
628
+ label: "Composition frozen",
629
+ summary: manifest.composition.compositionId
630
+ },
631
+ {
632
+ ...common,
633
+ id: `${manifest.releaseId}:loss`,
634
+ type: "L",
635
+ label: "Trade loss value",
636
+ summary: `MAX_LOSS_VALUE ${manifest.composition.maxLossValue}`,
637
+ maxLossValue: manifest.composition.maxLossValue
638
+ },
639
+ {
640
+ ...common,
641
+ id: `${manifest.releaseId}:deployment`,
642
+ type: "D",
643
+ label: "Release manifest",
644
+ summary: manifest.verdict.status,
645
+ severity: manifest.verdict.status === "READY_FOR_RUNTIME" ? "info" : "blocking"
646
+ },
647
+ {
648
+ ...common,
649
+ id: `${manifest.releaseId}:recommendation`,
650
+ type: "R",
651
+ label: "Release recommendation",
652
+ summary: manifest.verdict.summary,
653
+ severity: manifest.verdict.status === "READY_FOR_RUNTIME" ? "info" : "warning"
654
+ }
655
+ ];
656
+ };
657
+ var writeJsonAtomic = async (filePath, value) => {
658
+ const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
659
+ await import_promises.default.mkdir(import_node_path.default.dirname(filePath), { recursive: true });
660
+ await import_promises.default.writeFile(
661
+ temporaryPath,
662
+ `${canonicalStrategyReleaseJson(value)}
663
+ `,
664
+ "utf8"
665
+ );
666
+ await import_promises.default.rename(temporaryPath, filePath);
667
+ };
668
+ var publishStrategyRelease = async ({
669
+ rootDir,
670
+ manifest
671
+ }) => {
672
+ const envelope = createStrategyReleaseEnvelope(manifest);
673
+ const markerEnvelope = (0, import_strategyReleaseEvidence.createStrategyEvidenceMarkerEnvelope)({
674
+ strategy: manifest.strategy,
675
+ createdAt: manifest.createdAt,
676
+ markers: releaseMarkers(manifest),
677
+ sourceArtifacts: manifest.evidence.map((entry) => ({
678
+ artifactId: entry.artifactId,
679
+ sha256: entry.sha256,
680
+ path: entry.path
681
+ }))
682
+ });
683
+ const releasePath = import_node_path.default.join(
684
+ rootDir,
685
+ "releases",
686
+ safeSegment(manifest.strategy, "strategy"),
687
+ `${manifest.releaseId}.json`
688
+ );
689
+ const markerPath = import_node_path.default.join(
690
+ rootDir,
691
+ "markers",
692
+ safeSegment(manifest.strategy, "strategy"),
693
+ `${markerEnvelope.artifactId}.json`
694
+ );
695
+ await Promise.all([
696
+ writeJsonAtomic(releasePath, envelope),
697
+ writeJsonAtomic(markerPath, markerEnvelope)
698
+ ]);
699
+ await Promise.all([
700
+ verifyStrategyReleaseEnvelope(releasePath),
701
+ verifyStrategyEvidenceMarkerEnvelope(markerPath)
702
+ ]);
703
+ return { releasePath, markerPath, envelope, markerEnvelope };
704
+ };
705
+ var buildStrategyLiveDiagnosis = (input) => {
706
+ const minimumClosedTrades = input.minimumClosedTrades ?? 20;
707
+ const minimumParityRatio = input.minimumParityRatio ?? 0.95;
708
+ const maximumOrderFailureRate = input.maximumOrderFailureRate ?? 0.05;
709
+ const minimumRegimeCoverage = input.minimumRegimeCoverage ?? 0.5;
710
+ const hasExplicitRiskScale = input.riskScaleComparable != null;
711
+ const comparisonObservedDrawdown = hasExplicitRiskScale ? input.riskScaleComparable ? input.normalizedObservedDrawdown ?? null : null : input.observedDrawdown;
712
+ const runtimeDivergence = !input.lineageComparable || input.parityRatio != null && input.parityRatio < minimumParityRatio || input.orderFailureRate != null && input.orderFailureRate > maximumOrderFailureRate;
713
+ const insufficient = input.closedTrades < minimumClosedTrades || input.parityRatio == null || comparisonObservedDrawdown == null || input.historicalDrawdownP95 == null;
714
+ const expected = !runtimeDivergence && !insufficient && comparisonObservedDrawdown <= input.historicalDrawdownP95;
715
+ const verdict = runtimeDivergence ? "RUNTIME_DIVERGENCE" : insufficient ? "INSUFFICIENT_EVIDENCE" : expected ? "EXPECTED_DRAWDOWN" : "GENERALIZATION_FAILURE";
716
+ const attributionIncomplete = input.rawCoreExpectancyDelta == null || input.aiGateAddedValue == null || input.regimeCoverage == null;
717
+ const attributedVerdict = verdict === "GENERALIZATION_FAILURE" && attributionIncomplete ? "INSUFFICIENT_EVIDENCE" : verdict;
718
+ const subtype = attributedVerdict !== "GENERALIZATION_FAILURE" ? null : input.rawCoreExpectancyDelta < 0 && input.aiGateAddedValue >= 0 ? "RAW_CORE_DECAY" : input.aiGateAddedValue < 0 ? "AI_GATE_FAILURE" : input.regimeCoverage < minimumRegimeCoverage ? "REGIME_SHIFT" : (input.overfitProbability ?? 0) >= 0.5 ? "SUSPECTED_HISTORICAL_OVERFIT" : "RAW_CORE_DECAY";
719
+ const explanation = attributedVerdict === "RUNTIME_DIVERGENCE" ? "Runtime cannot be compared economically until lineage, replay parity, and order execution agree with the frozen composition." : attributedVerdict === "EXPECTED_DRAWDOWN" ? "The observed drawdown is within the preregistered equal-length historical drawdown envelope." : attributedVerdict === "GENERALIZATION_FAILURE" ? `Runtime is comparable, but the observed drawdown exceeds the historical envelope (${subtype}).` : attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "The drawdown breaches its historical envelope, but matched shadow raw-core, deterministic-gate, or causal regime evidence is missing for attribution." : "There are not enough comparable closed trades and historical drawdown observations for attribution.";
720
+ const recommendations = attributedVerdict === "RUNTIME_DIVERGENCE" ? [
721
+ "Resolve lineage and replay parity mismatches before evaluating strategy economics.",
722
+ "Inspect order failures and execution residuals against the frozen execution model."
723
+ ] : attributedVerdict === "EXPECTED_DRAWDOWN" ? [
724
+ "Keep the frozen composition unchanged and continue prospective evidence collection."
725
+ ] : attributedVerdict === "GENERALIZATION_FAILURE" ? [
726
+ "Do not retune the live cohort; open a new immutable research lineage.",
727
+ "Compare raw-core decay, AI-gate added value, and causal regime coverage."
728
+ ] : [
729
+ attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "Collect matched shadow raw-core, deterministic-gate, and causal regime evidence without changing the composition." : "Collect more verified prospective outcomes without changing the composition."
730
+ ];
731
+ return {
732
+ schema: import_types.STRATEGY_LIVE_DIAGNOSIS_SCHEMA,
733
+ strategy: input.strategy,
734
+ compositionId: input.compositionId,
735
+ createdAt: input.createdAt,
736
+ verdict: attributedVerdict,
737
+ subtype,
738
+ confidence: runtimeDivergence || input.closedTrades >= 50 ? "high" : input.closedTrades >= 20 ? "medium" : "low",
739
+ evidence: {
740
+ lineageComparable: input.lineageComparable,
741
+ riskScaleComparable: input.riskScaleComparable ?? false,
742
+ releaseMaxLossValue: input.releaseMaxLossValue ?? null,
743
+ runtimeMaxLossValue: input.runtimeMaxLossValue ?? null,
744
+ riskScaleRatio: input.riskScaleRatio ?? null,
745
+ parityRatio: input.parityRatio,
746
+ orderFailureRate: input.orderFailureRate,
747
+ observedDrawdown: input.observedDrawdown,
748
+ normalizedObservedDrawdown: input.normalizedObservedDrawdown ?? null,
749
+ historicalDrawdownP95: input.historicalDrawdownP95,
750
+ historicalDrawdownMaximum: input.historicalDrawdownMaximum,
751
+ closedTrades: input.closedTrades,
752
+ rawCoreExpectancyDelta: input.rawCoreExpectancyDelta,
753
+ aiGateAddedValue: input.aiGateAddedValue,
754
+ regimeCoverage: input.regimeCoverage,
755
+ overfitProbability: input.overfitProbability
756
+ },
757
+ explanation,
758
+ recommendations
759
+ };
760
+ };
761
+ var buildStrategyLiveDiagnosisFromScorecard = ({
762
+ manifest,
763
+ scorecard,
764
+ days
765
+ }) => {
766
+ const rolling = scorecard.rolling.find((entry) => entry.days === days);
767
+ const envelope = manifest.monitoring.drawdownEnvelopes.find(
768
+ (entry) => entry.days === days
769
+ );
770
+ const orderFailureRate = scorecard.funnel.orderAttempts ? scorecard.funnel.orderFailures / scorecard.funnel.orderAttempts : null;
771
+ const lineage = scorecard.lineage;
772
+ 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;
773
+ const releaseMaxLossValue = manifest.composition.maxLossValue;
774
+ const runtimeMaxLossValue = lineage?.maxLossValue ?? null;
775
+ const riskScaleComparable = Number.isFinite(releaseMaxLossValue) && releaseMaxLossValue > 0 && runtimeMaxLossValue != null && Number.isFinite(runtimeMaxLossValue) && runtimeMaxLossValue > 0;
776
+ const riskScaleRatio = riskScaleComparable ? runtimeMaxLossValue / releaseMaxLossValue : null;
777
+ const normalizedObservedDrawdown = rolling != null && riskScaleRatio != null ? rolling.maxDrawdown / riskScaleRatio : null;
778
+ const normalizeRuntimeEconomics = (value) => value != null && riskScaleRatio != null ? value / riskScaleRatio : null;
779
+ return buildStrategyLiveDiagnosis({
780
+ strategy: manifest.strategy,
781
+ compositionId: manifest.composition.compositionId,
782
+ createdAt: scorecard.generatedAt,
783
+ lineageComparable,
784
+ riskScaleComparable,
785
+ releaseMaxLossValue,
786
+ runtimeMaxLossValue,
787
+ riskScaleRatio,
788
+ parityRatio: scorecard.parity.ratio,
789
+ orderFailureRate,
790
+ observedDrawdown: rolling?.maxDrawdown ?? null,
791
+ normalizedObservedDrawdown,
792
+ historicalDrawdownP95: envelope?.p95 ?? null,
793
+ historicalDrawdownMaximum: envelope?.maximum ?? null,
794
+ closedTrades: rolling?.closedTrades ?? 0,
795
+ rawCoreExpectancyDelta: scorecard.prospective?.rawCoreExpectancy != null && manifest.monitoring.rawCoreExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) - manifest.monitoring.rawCoreExpectancy : null,
796
+ aiGateAddedValue: scorecard.prospective?.rawCoreExpectancy != null && scorecard.prospective.aiGateExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.aiGateExpectancy) - normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) : null,
797
+ regimeCoverage: scorecard.prospective?.regimeCoverage ?? null,
798
+ overfitProbability: manifest.monitoring.overfitProbability,
799
+ minimumClosedTrades: manifest.monitoring.minimumProspectiveClosedTrades,
800
+ minimumParityRatio: manifest.monitoring.minimumParityRatio,
801
+ maximumOrderFailureRate: manifest.monitoring.maximumOrderFailureRate,
802
+ minimumRegimeCoverage: manifest.monitoring.minimumRegimeCoverage
803
+ });
804
+ };
805
+ var realizedDrawdown = (trades, startIndex = 0, endIndex = trades.length) => {
806
+ let equity = 0;
807
+ let peak = 0;
808
+ let maximum = 0;
809
+ for (let index = startIndex; index < endIndex; index += 1) {
810
+ const trade = trades[index];
811
+ equity += trade.netProfit;
812
+ peak = Math.max(peak, equity);
813
+ maximum = Math.max(maximum, peak - equity);
814
+ }
815
+ return maximum;
816
+ };
817
+ var lowerBoundExitTimestamp = (trades, timestamp) => {
818
+ let left = 0;
819
+ let right = trades.length;
820
+ while (left < right) {
821
+ const middle = Math.floor((left + right) / 2);
822
+ if (trades[middle].exitTimestamp < timestamp) left = middle + 1;
823
+ else right = middle;
824
+ }
825
+ return left;
826
+ };
827
+ var percentile = (values, probability) => {
828
+ const sorted = [...values].sort((left, right) => left - right);
829
+ const position = (sorted.length - 1) * probability;
830
+ const lower = Math.floor(position);
831
+ const upper = Math.ceil(position);
832
+ if (lower === upper) return sorted[lower];
833
+ return sorted[lower] * (upper - position) + sorted[upper] * (position - lower);
834
+ };
835
+ var buildStrategyMonitoringProfile = ({
836
+ trades,
837
+ startTime,
838
+ endTime,
839
+ days,
840
+ minimumProspectiveClosedTrades = 20,
841
+ minimumParityRatio = 0.95,
842
+ maximumOrderFailureRate = 0.05,
843
+ minimumRegimeCoverage = 0.5,
844
+ aiGateExpectancy = null,
845
+ overfitProbability = null
846
+ }) => {
847
+ assert(endTime > startTime, "monitoring profile window is invalid");
848
+ assert(days.length > 0, "monitoring profile days are required");
849
+ const ordered = trades.filter(
850
+ (trade) => Number.isFinite(trade.exitTimestamp) && Number.isFinite(trade.netProfit) && trade.exitTimestamp >= startTime && trade.exitTimestamp < endTime
851
+ ).sort((left, right) => left.exitTimestamp - right.exitTimestamp);
852
+ const drawdownEnvelopes = [...new Set(days)].sort((left, right) => left - right).map((windowDays) => {
853
+ assert(
854
+ Number.isInteger(windowDays) && windowDays > 0,
855
+ "monitoring profile days must be positive integers"
856
+ );
857
+ const duration = windowDays * DAY_MS;
858
+ assert(
859
+ endTime - startTime >= duration,
860
+ `${windowDays}d monitoring window exceeds historical coverage`
861
+ );
862
+ const drawdowns = [];
863
+ for (let windowStart = startTime; windowStart + duration <= endTime; windowStart += DAY_MS) {
864
+ const windowEnd = windowStart + duration;
865
+ drawdowns.push(
866
+ realizedDrawdown(
867
+ ordered,
868
+ lowerBoundExitTimestamp(ordered, windowStart),
869
+ lowerBoundExitTimestamp(ordered, windowEnd)
870
+ )
871
+ );
872
+ }
873
+ return {
874
+ days: windowDays,
875
+ p95: percentile(drawdowns, 0.95),
876
+ maximum: Math.max(...drawdowns)
877
+ };
878
+ });
879
+ return {
880
+ minimumProspectiveClosedTrades,
881
+ minimumParityRatio,
882
+ maximumOrderFailureRate,
883
+ minimumRegimeCoverage,
884
+ drawdownEnvelopes,
885
+ rawCoreExpectancy: ordered.length ? ordered.reduce((sum, trade) => sum + trade.netProfit, 0) / ordered.length : null,
886
+ aiGateExpectancy,
887
+ overfitProbability
888
+ };
889
+ };
890
+ var publishStrategyLiveDiagnosis = async ({
891
+ rootDir,
892
+ diagnosis,
893
+ sourceArtifacts,
894
+ composition
895
+ }) => {
896
+ const diagnosisSha256 = strategyReleaseSha256(diagnosis);
897
+ const diagnosisId = `${safeSegment(diagnosis.strategy, "strategy")}_${compactTimestamp(diagnosis.createdAt)}_${diagnosisSha256.slice(0, 16)}`;
898
+ const envelope = {
899
+ schema: "tradejs-strategy-live-diagnosis-envelope/v1",
900
+ diagnosisId,
901
+ diagnosisSha256,
902
+ diagnosis
903
+ };
904
+ const common = {
905
+ timestamp: diagnosis.createdAt,
906
+ artifactId: diagnosisId,
907
+ artifactSha256: diagnosisSha256,
908
+ compositionId: diagnosis.compositionId,
909
+ ...composition ? {
910
+ gitSha: composition.gitSha,
911
+ configFingerprint: composition.runtimeConfigFingerprint,
912
+ gateFingerprint: composition.gateFingerprint,
913
+ contextFingerprint: composition.runtimeContextFingerprint,
914
+ maxLossValue: composition.maxLossValue
915
+ } : {}
916
+ };
917
+ const markers = [
918
+ ...diagnosis.evidence.runtimeMaxLossValue != null && diagnosis.evidence.runtimeMaxLossValue !== diagnosis.evidence.releaseMaxLossValue ? [
919
+ {
920
+ ...common,
921
+ id: `${diagnosisId}:loss`,
922
+ type: "L",
923
+ label: "Trade loss value changed",
924
+ summary: `MAX_LOSS_VALUE ${diagnosis.evidence.runtimeMaxLossValue}`,
925
+ maxLossValue: diagnosis.evidence.runtimeMaxLossValue,
926
+ severity: "info"
927
+ }
928
+ ] : [],
929
+ ...diagnosis.verdict === "RUNTIME_DIVERGENCE" ? [
930
+ {
931
+ ...common,
932
+ id: `${diagnosisId}:parity`,
933
+ type: "P",
934
+ label: "Runtime parity mismatch",
935
+ summary: diagnosis.explanation,
936
+ severity: "blocking"
937
+ }
938
+ ] : [],
939
+ {
940
+ ...common,
941
+ id: `${diagnosisId}:recommendation`,
942
+ type: "R",
943
+ label: "Live diagnosis",
944
+ summary: `${diagnosis.verdict}: ${diagnosis.explanation}`,
945
+ severity: diagnosis.verdict === "EXPECTED_DRAWDOWN" ? "info" : "warning"
946
+ }
947
+ ];
948
+ const markerEnvelope = (0, import_strategyReleaseEvidence.createStrategyEvidenceMarkerEnvelope)({
949
+ strategy: diagnosis.strategy,
950
+ createdAt: diagnosis.createdAt,
951
+ markers,
952
+ sourceArtifacts
953
+ });
954
+ const diagnosisPath = import_node_path.default.join(
955
+ rootDir,
956
+ "diagnoses",
957
+ safeSegment(diagnosis.strategy, "strategy"),
958
+ `${diagnosisId}.json`
959
+ );
960
+ const markerPath = import_node_path.default.join(
961
+ rootDir,
962
+ "markers",
963
+ safeSegment(diagnosis.strategy, "strategy"),
964
+ `${markerEnvelope.artifactId}.json`
965
+ );
966
+ await Promise.all([
967
+ writeJsonAtomic(diagnosisPath, envelope),
968
+ writeJsonAtomic(markerPath, markerEnvelope)
969
+ ]);
970
+ await verifyStrategyEvidenceMarkerEnvelope(markerPath);
971
+ return { diagnosisPath, markerPath, envelope, markerEnvelope };
972
+ };
973
+ var planStrategyEvidenceRetention = ({
974
+ now,
975
+ entries,
976
+ retentionDays = {
977
+ operational_redis: 3,
978
+ verbose_payload: 14,
979
+ verified_runtime_bundle: 90,
980
+ compact_ledger: null
981
+ }
982
+ }) => {
983
+ const keep = [];
984
+ const remove = [];
985
+ for (const entry of entries) {
986
+ const days = retentionDays[entry.kind];
987
+ if (days == null || !entry.verified || !entry.aggregated || now - entry.createdAt <= days * DAY_MS) {
988
+ keep.push(entry);
989
+ } else {
990
+ remove.push(entry);
991
+ }
992
+ }
993
+ return {
994
+ keep,
995
+ delete: remove,
996
+ bytesReclaimable: remove.reduce((total, entry) => total + entry.bytes, 0)
997
+ };
998
+ };
999
+ var finiteString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
1000
+ var finiteNumber = (value) => {
1001
+ const number = Number(value);
1002
+ return Number.isFinite(number) ? number : null;
1003
+ };
1004
+ var uniqueStrings = (values) => [
1005
+ ...new Set(
1006
+ values.map(finiteString).filter((value) => value != null)
1007
+ )
1008
+ ];
1009
+ var incompleteEvidenceLineage = () => ({
1010
+ strategy: null,
1011
+ gitSha: null,
1012
+ gitDirty: null,
1013
+ coreConfigSha256: null,
1014
+ gateConfigIdsFingerprint: null,
1015
+ gateFingerprint: null,
1016
+ runtimeConfigFingerprint: null,
1017
+ gateContextFingerprint: null,
1018
+ runtimeContextFingerprint: null,
1019
+ maxLossValue: null,
1020
+ sourceSha256s: []
1021
+ });
1022
+ var extractEvidenceLineage = ({
1023
+ artifact,
1024
+ reference,
1025
+ nested,
1026
+ array
1027
+ }) => {
1028
+ const empty = incompleteEvidenceLineage();
1029
+ if (reference.kind === "core_research") {
1030
+ const variants = array(artifact.variants).map(nested).filter((entry) => entry != null);
1031
+ const selectedCandidateIds = uniqueStrings(
1032
+ array(artifact.comparisons).map(nested).filter(
1033
+ (comparison) => comparison != null && nested(comparison.selection)?.passed === true
1034
+ ).map((comparison) => comparison.candidateId)
1035
+ );
1036
+ const selectedVariants = selectedCandidateIds.length === 1 ? variants.filter(
1037
+ (variant) => finiteString(nested(variant.variant)?.id) === selectedCandidateIds[0]
1038
+ ) : [];
1039
+ const configShas = uniqueStrings(
1040
+ selectedVariants.map((variant) => nested(variant.variant)?.configSha256)
1041
+ );
1042
+ const maxLossValues = [
1043
+ ...new Set(
1044
+ selectedVariants.map(
1045
+ (variant) => finiteNumber(
1046
+ nested(nested(variant.variant)?.resolvedConfig)?.MAX_LOSS_VALUE
1047
+ )
1048
+ ).filter((value) => value != null)
1049
+ )
1050
+ ];
1051
+ const sourceSha256s = uniqueStrings(
1052
+ selectedVariants.flatMap(
1053
+ (variant) => array(variant.files).map((file) => nested(file)?.sha256)
1054
+ )
1055
+ );
1056
+ const resultLineage = nested(artifact.lineage);
1057
+ const strategies = uniqueStrings(
1058
+ selectedVariants.map(
1059
+ (variant) => finiteString(nested(variant.variant)?.configName)?.split(":")[0]
1060
+ )
1061
+ );
1062
+ return {
1063
+ ...empty,
1064
+ strategy: strategies.length === 1 ? strategies[0] : null,
1065
+ gitSha: finiteString(resultLineage?.gitSha),
1066
+ gitDirty: resultLineage?.gitSha != null ? finiteString(resultLineage?.dirtyDiffSha256) != null : null,
1067
+ coreConfigSha256: configShas.length === 1 ? configShas[0] : null,
1068
+ maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null,
1069
+ sourceSha256s
1070
+ };
1071
+ }
1072
+ if (reference.kind === "ai_gate") {
1073
+ const lineage = nested(nested(artifact.research)?.lineage);
1074
+ return {
1075
+ ...empty,
1076
+ strategy: finiteString(nested(artifact.run)?.strategy),
1077
+ gitSha: finiteString(lineage?.gitSha),
1078
+ gitDirty: typeof lineage?.gitDirty === "boolean" ? lineage.gitDirty : null,
1079
+ gateConfigIdsFingerprint: finiteString(lineage?.configIdsFingerprint),
1080
+ gateFingerprint: finiteString(lineage?.gateFingerprint),
1081
+ gateContextFingerprint: finiteString(lineage?.contextFingerprint),
1082
+ sourceSha256s: uniqueStrings(array(lineage?.sourceSha256s))
1083
+ };
1084
+ }
1085
+ if (reference.kind === "runtime_parity") {
1086
+ const comparison = nested(nested(artifact.replay)?.runtimeComparison);
1087
+ const replayLineages = array(nested(comparison?.lineage)?.replay).map(nested).map((entry) => nested(entry?.lineage)).filter((entry) => entry != null);
1088
+ const one = (key) => {
1089
+ const values = uniqueStrings(replayLineages.map((entry) => entry[key]));
1090
+ return values.length === 1 ? values[0] : null;
1091
+ };
1092
+ const maxLossValues = [
1093
+ ...new Set(
1094
+ replayLineages.map((entry) => finiteNumber(entry.maxLossValue)).filter((value) => value != null)
1095
+ )
1096
+ ];
1097
+ const dirtyValues = [
1098
+ ...new Set(
1099
+ replayLineages.map((entry) => entry.gitDirty).filter((value) => typeof value === "boolean")
1100
+ )
1101
+ ];
1102
+ return {
1103
+ ...empty,
1104
+ strategy: (() => {
1105
+ const strategies = uniqueStrings(
1106
+ array(nested(comparison?.lineage)?.replay).map(
1107
+ (entry) => nested(entry)?.strategy
1108
+ )
1109
+ );
1110
+ return strategies.length === 1 ? strategies[0] : null;
1111
+ })(),
1112
+ gitSha: one("gitSha"),
1113
+ gitDirty: dirtyValues.length === 1 ? dirtyValues[0] : null,
1114
+ gateFingerprint: one("gateFingerprint"),
1115
+ runtimeConfigFingerprint: one("configFingerprint"),
1116
+ runtimeContextFingerprint: one("contextFingerprint"),
1117
+ maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null
1118
+ };
1119
+ }
1120
+ if (reference.kind === "execution_calibration") {
1121
+ const samples = [
1122
+ ...array(artifact.samples),
1123
+ ...array(artifact.replayOnlySamples)
1124
+ ].map(nested);
1125
+ const lineages = samples.map((sample) => nested(sample?.runtimeLineage)).filter((lineage) => lineage != null);
1126
+ const one = (key) => {
1127
+ const values = uniqueStrings(lineages.map((lineage) => lineage[key]));
1128
+ return values.length === 1 ? values[0] : null;
1129
+ };
1130
+ const dirtyValues = [
1131
+ ...new Set(
1132
+ lineages.map((lineage) => lineage.gitDirty).filter((value) => typeof value === "boolean")
1133
+ )
1134
+ ];
1135
+ const maxLossValues = [
1136
+ ...new Set(
1137
+ lineages.map((lineage) => finiteNumber(lineage.maxLossValue)).filter((value) => value != null)
1138
+ )
1139
+ ];
1140
+ return {
1141
+ ...empty,
1142
+ strategy: (() => {
1143
+ const strategies = uniqueStrings(
1144
+ samples.map((sample) => sample?.strategy)
1145
+ );
1146
+ return strategies.length === 1 ? strategies[0] : null;
1147
+ })(),
1148
+ gitSha: one("gitSha"),
1149
+ gitDirty: dirtyValues.length === 1 ? dirtyValues[0] : null,
1150
+ gateFingerprint: one("gateFingerprint"),
1151
+ runtimeConfigFingerprint: one("configFingerprint"),
1152
+ runtimeContextFingerprint: one("contextFingerprint"),
1153
+ maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null,
1154
+ sourceSha256s: uniqueStrings([
1155
+ ...array(nested(artifact.sources)?.sha256s),
1156
+ ...array(artifact.sourceSha256s)
1157
+ ])
1158
+ };
1159
+ }
1160
+ return empty;
1161
+ };
1162
+ var verifyCompletedCoreResearchBundle = async ({
1163
+ artifact,
1164
+ resultPath,
1165
+ resultSha256
1166
+ }) => {
1167
+ const researchId = finiteString(artifact.researchId);
1168
+ const specSha256 = finiteString(artifact.specSha256);
1169
+ assert(researchId != null, "core result researchId is required");
1170
+ assert(specSha256 != null, "core result specSha256 is required");
1171
+ const researchDir = import_node_path.default.dirname(resultPath);
1172
+ const manifestPath = import_node_path.default.join(researchDir, "manifest.json");
1173
+ const specPath = import_node_path.default.join(researchDir, "spec.json");
1174
+ const manifestBytes = await import_promises.default.readFile(manifestPath).catch(() => null);
1175
+ const specBytes = await import_promises.default.readFile(specPath).catch(() => null);
1176
+ assert(
1177
+ manifestBytes != null && specBytes != null,
1178
+ "core result must belong to a completed core research bundle"
1179
+ );
1180
+ const manifest = JSON.parse(manifestBytes.toString("utf8"));
1181
+ const artifactHashes = manifest.artifactHashes != null && typeof manifest.artifactHashes === "object" && !Array.isArray(manifest.artifactHashes) ? manifest.artifactHashes : null;
1182
+ const parsedSpec = JSON.parse(specBytes.toString("utf8"));
1183
+ assert(
1184
+ manifest.schema === "tradejs-core-research-manifest/v1" && manifest.status === "completed" && manifest.researchId === researchId && manifest.specSha256 === specSha256 && artifactHashes?.["result.json"] === resultSha256 && (0, import_node_crypto.createHash)("sha256").update(canonicalStrategyReleaseJson(parsedSpec)).digest("hex") === specSha256,
1185
+ "core result is not bound by its completed core research manifest"
1186
+ );
1187
+ for (const [relativePath, expectedSha256] of Object.entries(
1188
+ artifactHashes ?? {}
1189
+ )) {
1190
+ assert(
1191
+ !import_node_path.default.isAbsolute(relativePath) && !relativePath.split(/[\\/]/).includes(".."),
1192
+ `core bundle artifact ${relativePath} escapes its research directory`
1193
+ );
1194
+ assert(
1195
+ typeof expectedSha256 === "string" && SHA256_RE.test(expectedSha256),
1196
+ `core bundle artifact ${relativePath} has an invalid checksum`
1197
+ );
1198
+ const actualSha256 = await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(
1199
+ import_node_path.default.join(researchDir, relativePath)
1200
+ );
1201
+ assert(
1202
+ actualSha256 === expectedSha256,
1203
+ `core bundle artifact ${relativePath} checksum mismatch`
1204
+ );
1205
+ }
1206
+ };
1207
+ async function collectReleaseEvidenceReferences(references) {
1208
+ return Promise.all(
1209
+ references.map(async (reference) => {
1210
+ const actualSha256 = await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(reference.path);
1211
+ assert(
1212
+ actualSha256 === reference.sha256,
1213
+ `evidence ${reference.artifactId} checksum mismatch`
1214
+ );
1215
+ let artifact;
1216
+ try {
1217
+ const parsed = JSON.parse(
1218
+ await import_promises.default.readFile(reference.path, "utf8")
1219
+ );
1220
+ assert(
1221
+ parsed != null && typeof parsed === "object" && !Array.isArray(parsed),
1222
+ `evidence ${reference.artifactId} must be a JSON object`
1223
+ );
1224
+ artifact = parsed;
1225
+ } catch (error) {
1226
+ throw new Error(
1227
+ `Invalid strategy release: evidence ${reference.artifactId} is not valid JSON: ${error.message}`
1228
+ );
1229
+ }
1230
+ const nested = (value) => value != null && typeof value === "object" && !Array.isArray(value) ? value : null;
1231
+ const array = (value) => Array.isArray(value) ? value : [];
1232
+ const semanticallyValid = reference.kind === "core_research" ? artifact.schema === "tradejs-core-research-result/v1" || artifact.schema === "tradejs-core-research-manifest/v1" && artifact.status === "completed" : reference.kind === "ai_gate" ? nested(artifact.run)?.mode === "local-deterministic" && nested(artifact.research)?.lineage != null && nested(artifact.outcome) != null : reference.kind === "runtime_parity" ? artifact.reportType === "replay-runtime-evidence" && nested(nested(artifact.replay)?.runtimeComparison) != null : reference.kind === "execution_calibration" ? artifact.reportType === "execution-calibration" && nested(artifact.summary) != null : reference.kind === "runtime_evidence" ? artifact.reportType === "runtime-evidence" : artifact.reportType === "runtime-scorecard";
1233
+ assert(
1234
+ semanticallyValid,
1235
+ `evidence ${reference.artifactId} does not match ${reference.kind}`
1236
+ );
1237
+ if (reference.kind === "core_research" && artifact.schema === "tradejs-core-research-result/v1") {
1238
+ await verifyCompletedCoreResearchBundle({
1239
+ artifact,
1240
+ resultPath: reference.path,
1241
+ resultSha256: actualSha256
1242
+ });
1243
+ }
1244
+ const releaseAssertions = (() => {
1245
+ if (reference.kind === "core_research") {
1246
+ const evidence = nested(artifact.evidence);
1247
+ const comparisons = array(artifact.comparisons).map(nested).filter((entry) => entry != null);
1248
+ const variants = array(artifact.variants).map(nested).filter((entry) => entry != null);
1249
+ const hasCohorts = (value) => {
1250
+ const cohorts = nested(value);
1251
+ return ["ALL", "LONG", "SHORT"].every(
1252
+ (cohort) => nested(cohorts?.[cohort]) != null
1253
+ );
1254
+ };
1255
+ const directionalEvidenceComplete = variants.length > 0 && variants.every(
1256
+ (variant) => hasCohorts(nested(variant.full)?.cohorts) && array(variant.terminal).every(
1257
+ (window) => hasCohorts(nested(window)?.cohorts)
1258
+ ) && array(variant.folds).every(
1259
+ (window) => hasCohorts(nested(window)?.cohorts)
1260
+ )
1261
+ );
1262
+ const evidenceComplete = [
1263
+ "terminals",
1264
+ "folds",
1265
+ "coldStart",
1266
+ "costStress",
1267
+ "delayStress",
1268
+ "fastNonFast",
1269
+ "runtimeParity"
1270
+ ].every((key) => evidence?.[key] === "present");
1271
+ const reconciled = variants.length > 0 && variants.every(
1272
+ (variant) => nested(variant.reconciliation)?.status === "match"
1273
+ );
1274
+ const selectedCandidateIds = uniqueStrings(
1275
+ comparisons.filter(
1276
+ (comparison) => nested(comparison.selection)?.passed === true
1277
+ ).map((comparison) => comparison.candidateId)
1278
+ );
1279
+ const selected = selectedCandidateIds.length === 1;
1280
+ const finalStage = artifact.stage === "isolated_long" || artifact.stage === "confirmation";
1281
+ const verified = finalStage && evidenceComplete && directionalEvidenceComplete && reconciled && selected;
1282
+ return {
1283
+ coreEdgeVerified: verified,
1284
+ currentMarketSuitable: verified
1285
+ };
1286
+ }
1287
+ if (reference.kind === "ai_gate") {
1288
+ const outcome = nested(artifact.outcome);
1289
+ const approvedRisk = nested(outcome?.approvedRisk);
1290
+ const terminalWindows = array(
1291
+ nested(artifact.research)?.terminalWindows
1292
+ ).map(nested).filter((entry) => entry != null);
1293
+ const hasLongShortDirections = (value) => {
1294
+ const directions = new Map(
1295
+ array(value).map(nested).filter(
1296
+ (entry) => entry != null
1297
+ ).map((entry) => [finiteString(entry.direction), entry])
1298
+ );
1299
+ return ["LONG", "SHORT"].every(
1300
+ (direction) => nested(directions.get(direction)?.summary) != null
1301
+ );
1302
+ };
1303
+ const directionalEvidenceComplete = hasLongShortDirections(artifact.byDirection) && terminalWindows.every(
1304
+ (window) => hasLongShortDirections(window.byDirection)
1305
+ );
1306
+ const terminalsPass = terminalWindows.length > 0 && terminalWindows.every((window) => {
1307
+ const terminalOutcome = nested(window.outcome);
1308
+ return window.complete === true && Number(terminalOutcome?.approved ?? 0) > 0 && Number(
1309
+ nested(terminalOutcome?.approvedRisk)?.totalProfit ?? Number.NEGATIVE_INFINITY
1310
+ ) > 0;
1311
+ });
1312
+ return {
1313
+ aiGateAddsValue: Number(outcome?.expectancyDelta ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.totalProfit ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.profitFactor ?? Number.NEGATIVE_INFINITY) > 1 && directionalEvidenceComplete && terminalsPass
1314
+ };
1315
+ }
1316
+ if (reference.kind === "runtime_parity") {
1317
+ const comparison = nested(nested(artifact.replay)?.runtimeComparison);
1318
+ const counts = nested(comparison?.counts);
1319
+ const matched = Number(counts?.matched ?? 0);
1320
+ const backtestOnly = Number(counts?.backtestOnly ?? 0);
1321
+ const runtimeOnly = Number(counts?.runtimeOnly ?? 0);
1322
+ return {
1323
+ runtimeParityVerified: matched > 0 && backtestOnly === 0 && runtimeOnly === 0 && nested(comparison?.lineage)?.reason == null
1324
+ };
1325
+ }
1326
+ if (reference.kind === "execution_calibration") {
1327
+ const counts = nested(artifact.counts);
1328
+ const all = nested(nested(artifact.summary)?.all);
1329
+ const residual = Number(
1330
+ nested(all?.residualVsCurrentModelBps)?.avg ?? Number.NaN
1331
+ );
1332
+ return {
1333
+ executionModelVerified: Number(counts?.fullTelemetryTrades ?? 0) > 0 && Number.isFinite(residual) && Math.abs(residual) <= 3
1334
+ };
1335
+ }
1336
+ return {};
1337
+ })();
1338
+ const lineage = extractEvidenceLineage({
1339
+ artifact,
1340
+ reference,
1341
+ nested,
1342
+ array
1343
+ });
1344
+ return { ...reference, verified: true, lineage, releaseAssertions };
1345
+ })
1346
+ );
1347
+ }
1348
+ // Annotate the CommonJS export names for ESM import in node:
1349
+ 0 && (module.exports = {
1350
+ buildStrategyLiveDiagnosis,
1351
+ buildStrategyLiveDiagnosisFromScorecard,
1352
+ buildStrategyMonitoringProfile,
1353
+ canonicalStrategyReleaseJson,
1354
+ collectReleaseEvidenceReferences,
1355
+ createStrategyEvidenceMarkerEnvelope,
1356
+ createStrategyReleaseEnvelope,
1357
+ createStrategyReleaseManifest,
1358
+ deriveStrategyReleaseResearchDecision,
1359
+ planStrategyEvidenceRetention,
1360
+ publishStrategyLiveDiagnosis,
1361
+ publishStrategyRelease,
1362
+ strategyReleaseSha256,
1363
+ verifyStrategyEvidenceMarkerEnvelope,
1364
+ verifyStrategyReleaseEnvelope
1365
+ });