@tradejs/cli 2.0.19 → 2.0.21

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