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